Java Web 高级编程 - 第四章 使用JSP显式页面内容

本章内容

 

JavaServer Pages

 

使用<br/>替代output.println("<br/>")

创建第一个JSP

1.了解文件结构

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

特性language将告诉容器JSP中使用的是哪种脚本语言。JSP脚本语言是一种可以内嵌在JSP中、用于完成某些操作的语言。

目前JSP只支持Java作为它的脚本语言,但该特性可以用于支持将来的扩展。

从技术上讲,可以忽略这个特性。因为Java是唯一得到支持的JSP脚本语言,而且在规范中Java也是默认的脚本语言,如果该特性不存在,就表示当前JSP将使用Java作为它的脚本语言。

特性contentType将告诉容器在发送响应时如何设置其中Content-Type头的值。Content-Type头同时包含了内容类型和字符编码,以分号隔开。

2.指令、声明、脚本和表达式

<%@ 这是一个指令 %>
<%! 这是一个声明 %>
<%  这是一个脚本 %>
<%= 这是一个表达式 %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
    private final int five = 0;

    protected String cowboy = "rodeo";

    //下面是赋值语句而不是声明语句,如果未注释的话会出现语法错误
    //cowboy = "test";

    public long addFive(long number)
    {
        return number + 5L;
    }

    public class MyInnerClass
    {

    }
    MyInnerClass instanceVariable = new MyInnerClass();

    //WeirdClassWithinMethod 在方法作用域内,所以如果未注释的话,下面的声明会出现语法错误
    //WeirdClassWithinMethod bad = new WeirdClassWithinMethod();
%>
<%
    class WeirdClassWithinMethod
    {

    }
    WeirdClassWithinMethod weirdClass = new WeirdClassWithinMethod();
    MyInnerClass innerClass = new MyInnerClass();
    int seven;
    seven = 7;
%>
<%= "Hello, World" %><br />
<%= addFive(12L) %>

3.注释代码 

<!-- 这是一个HTML/XML注释 -->
<%-- 这是一个JSP注释 --%>

标准XML和HTML注释会被浏览器忽略,但是还是会出现在响应的源代码中。

更重要的是,注释中的任何JSP标签都将被处理。

<!-- 这是一个HTML/XML注释:<%=someObject.dumpInfo() %> -->

如果someObject.sumpInfo()返回的是"connections=5;error=12;successes=3847",那么返回到客户端浏览器的响应也将包含以下HTML注释:

<!-- 这是一个HTML/XML注释:connections=5;error=12;successes=3847 -->

4.在JSP中导入类

在Java中使用某个类时,必须使用它的完全限定类名引用它,或者在Java代码文件的顶部添加一条导入语句。该规则在JSP中也是相同的。

<%@ page import="java.util.*,java.io.IOException" %>

需要注意的是,对于不产生输出的JSP标记、指令、声明和脚本,它们将会在客户端输出一行空白。所以,如果在变量声明和脚本之家鸟有许多导入类的page指令,那么将会在输出中显示出数行空白。为了解决这个问题,JSP开发者通常会将一个标记的尾部与另一个标记的头部连接在一起。

<%@ page import="java.util.Map" %><%@ page import="java.util.List" %><%@ page import="java.io.IOException" %>

后面还会介绍如何通过部署描述符设置去除所有的空白。

5.使用指令

pageEncoding

session:表示JSP是否将参与HTTP会话。默认值为true。

isELIgnored:该特性表示JSP编译器是否将解析和转换JSP中的表达式语言。

buffer和autoFlush:它们决定了JSP的输出方式:是在生成之后立即发送到浏览器中,还是先将输出缓存起来,再按批次发送到浏览器。buffer指定了JSP缓存的大小,autoFlush表示是否在它达到大小限制后自动刷新缓存。如果将buffer设置为"none",autoFlush设置为false,那么在将JSP转换成Java时将会出现异常。同样,如果将autoFlush设置为false,将buffer设置为满,同样会出现异常。

当autoFlush设置为true,缓存值越小,数据被刷新到客户端的频率就越高,反之缓存越大,数据刷新到客户端的频率就越低。

如果将buffer设置为"none"完全禁止缓存,则可以提高JSP的性能,因为它减少了内存占用和CPU花销。但是,如果不使用缓存,可能会导致向浏览器发送更多的数据包,并增加一定的带宽消耗。

errorPage:如果在JSP的执行过程中出现了错误,该特性将告诉容器应该将请求转发到哪个JSP。

isErrorPage:该特性表示当前的JSP是否被用作错误页面。如果设置为true,在该JSP中可以使用隐式的exception变量。

isThreadSafe:默认值为true。该特性表示当前的JSP可以安全地同时处理多个请求。如果修改为false,容器将把请求诸葛发送到该JSP。

作为一条好的经验法则:永远不要修改这个值。“如果你的JSP不是线程安全的,那么它就是错误的”。

extends:该特性指定了当前JSP Servlet的父类。使用该特性的JSP将无法从一个Web容器迁移到另一个容器,它也不是必须使用的。所以不要使用它。

include指令:

<%@ include file="index.jsp" %>
<jsp:include page="index.jsp" />

包含标签库:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

特性uri指定了目标标签库所属的URI命名空间,特性prefix则定义了用于引用库中标签时所使用的别名。

6.使用<jsp>标签

The Java EE 5 Tutorial

JavaServer Pages Standard Tag Library 1.1 Tag Reference

 

在JSP中使用Java(以及不鼓励使用Java的原因)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
    private static final String DEFAULT_USER = "Guest";
%>
<%
    String user = request.getParameter("user");
    if(user == null)
        user = DEFAULT_USER;
%>
<!DOCTYPE html>
<html>
    <head>
        <title>Hello User Application</title>
    </head>
    <body>
        Hello, <%= user %>!<br /><br />
        <form action="greeting.jsp" method="POST">
            Enter your name:<br />
            <input type="text" name="user" /><br />
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>

以上代码与之前章节中编写的HelloServllet.java相比较。更加简练,但完成了相同的任务。

之前在Hello-User项目中还创建了一个使用多值参数的Servlet。该功能也可以在JSP中实现。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
    <head>
        <title>Hello User Application</title>
    </head>
    <body>
        <form action="checkboxesSubmit.jsp" method="POST">
            Select the fruits you like to eat:<br />
            <input type="checkbox" name="fruit" value="Banana" /> Banana<br />
            <input type="checkbox" name="fruit" value="Apple" /> Apple<br />
            <input type="checkbox" name="fruit" value="Orange" /> Orange<br />
            <input type="checkbox" name="fruit" value="Guava" /> Guava<br />
            <input type="checkbox" name="fruit" value="Kiwi" /> Kiwi<br />
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>

该文件的输出与之前Hello-User项目中的MultiValueParameterServlet.java的doGet方法一致。

接下来创建checkboxesSubmit.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String[] fruits = request.getParameterValues("fruit");
%>
<!DOCTYPE html>
<html>
    <head>
        <title>Hello User Application</title>
    </head>
    <body>
        <h2>Your Selections</h2>
        <%
            if(fruits == null)
            {
        %>You did not select any fruits.<%
            }
            else
            {
        %><ul><%
                for(String fruit : fruits)
                {
                    out.println("<li>" + fruit + "</li>");
                }
        %></ul><%
            }
        %>
    </body>
</html>

粗体代码中脚本域代码交叉使用,在有逻辑需要的时候只使用Java代码,并且使用脚本直接输出内容,而不是使用隐式变量out。

最后,创建文件contextParameters.jsp,在其中使用application隐式变量并获取上下文初始化参数。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
    <head>
        <title>Hello User Application</title>
    </head>
    <body>
        settingOne: <%= application.getInitParameter("settingOne") %>,
        settingTwo: <%= application.getInitParameter("settingTwo") %>
    </body>
</html>

 

不应该在JSP中使用Java的原因:

几乎任何在普通Java类中可以完成的事情都可以在JSP中完成。

JavaServerPages是一门用于开发表示层的技术。尽管可以在表示层中混合数据库访问操作或数学计算,但并不是一个好主意。

在大多数组织中,用户界面开发者将负责创建表示层。这些开发者几乎没有编写Java代码的经验,因此为它们提供这样的能力是很危险的。

在一个具有良好结构。干净代码的应用程序中,表示层通常应该与业务层分开,同样也与数据持久层分隔开。

 

结合使用Servlet和JSP

1.配置部署描述符中的JSP属性

在web.xml文件中:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <display-name>Customer Support Application</display-name>

    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <url-pattern>*.jspf</url-pattern>
            <page-encoding>UTF-8</page-encoding>
            <scripting-invalid>false</scripting-invalid>
            <include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
            <trim-directive-whitespaces>true</trim-directive-whitespaces>
            <default-content-type>text/html</default-content-type>
        </jsp-property-group>
    </jsp-config>

</web-app>

(1).了解JSP属性组

标签<jsp-config>中可以包含任意数目的<jsp-property-group>标签。这些属性组用于区别不同JSP组的属性。例如,为/WEB_INF/jsp/admin文件夹中的所有JSP定义一组通用的属性,而为/WEB_INF/jsp/help文件夹中的所有JSP定义另一组属性。通过为每一个<jsp-property-group>定义不同的<url-pattern>标签来区分不同的属性组。

上面的示例中,<url-pattern>标签标识该属性组将应用于所有以.jsp和.jspf结尾的文件中,无论在Web应用程序的什么位置。

<jsp-property-group>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspf</url-pattern>
    <page-encoding>UTF-8</page-encoding>
    <include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
</jsp-property-group>
<jsp-property-group>
    <url-pattern>/WEB-INF/jsp/admin/*.jsp</url-pattern>
    <url-pattern>/WEB-INF/jsp/admin/*.jspf</url-pattern>
    <page-encoding>ISO-8859-1</page-encoding>
    <include-prelude>/WEB-INF/jsp/admin/base.jspf</include-prelude>
</jsp-property-group>

 

文件/WEB-INF/jsp/user.jsp只能匹配第一个属性组中的<url_pattern>。所以他的字符变量将被设定为UTF-8,并且/WEB-INF/jsp/base.jspf文件将被包含在他的开头。

另一个文件/WEB-INF/jsp/admin/user.jsp则可以匹配两个属性组。因为第二个属性组的匹配更加精确,所以该文件的字符编码将被设置为ISO-8859-1。

不过/WEB-INF/jsp/base.jspf和/WEB-INF/jsp/admin/base.jspf这两个文件都将被包含到该文件的开头.

(2).使用JSP属性

The Java EE 5 Tutorial

JavaServer Pages Standard Tag Library 1.1 Tag Reference

2.将Servlet中的请求转发给JSP

结合使用Servlet和JSP时的一种典型模式就是,有Servlet接受请求,实现业务逻辑处理以及必要的数据存储和读取,创建可以由JSP轻松处理的数据模型,最终将请求转发给JSP。

(1).使用请求派发器

private void showTicketForm(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
{
    request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp").forward(request, response);
}

 

这个方法的代码引入了HttpServletRequest的一个新特性。通过方法getRequestDispatcher可以获得一个RequestDispatcher,可以用于处理针对指定路径下的内部转发和包含。通过这个对象,可以将当前请求转发给调用forward方法的jsp。

注意,这不是重定向,用户的浏览器不回收到重定向状态码,浏览器的URL也不会改变。

(2).设计表示层

<%@ page session="false" %>
<%
    String ticketId = (String)request.getAttribute("ticketId");
    Ticket ticket = (Ticket)request.getAttribute("ticket");
%>
<!DOCTYPE html>
<html>
    <head>
        <title>Customer Support</title>
    </head>
    <body>
        <h2>Ticket #<%= ticketId %>: <%= ticket.getSubject() %></h2>
        <i>Customer Name - <%= ticket.getCustomerName() %></i><br /><br />
        <%= ticket.getBody() %><br /><br />
        <%
            if(ticket.getNumberOfAttachments() > 0)
            {
                %>Attachments: <%
                int i = 0;
                for(Attachment a : ticket.getAttachments())
                {
                    if(i++ > 0)
                        out.print(", ");
                    %><a href="<c:url value="/tickets">
                        <c:param name="action" value="download" />
                        <c:param name="ticketId" value="<%= ticketId %>" />
                        <c:param name="attachment" value="<%= a.getName() %>" />
                    </c:url>"><%= a.getName() %></a><%
                }
                %><br /><br /><%
            }
        %>
        <a href="<c:url value="/tickets" />">Return to list tickets</a>
    </body>
</html>

 

这个JSP作为表示层,需要得到ticketId和ticket才能正确显示出页面。

private void viewTicket(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
{
    String idString = request.getParameter("ticketId");
    Ticket ticket = this.getTicket(idString, response);
    if(ticket == null)
        return;

    request.setAttribute("ticketId", idString);
    request.setAttribute("ticket", ticket);

    request.getRequestDispatcher("/WEB-INF/jsp/view/viewTicket.jsp").forward(request, response);
}

 

该方法的前几行代码执行了一些业务逻辑,解析请求参数并从数据库中得到ticket。然后粗体代码在请求中添加了两个特性。这是使用请求特性的主要目的。它们可用在应用程序的不同部分(它们处理的必须是相同的请求)之间传递数据,例如在Servlet和JSP之间。请求特性不同区请求参数:请求特性是对象,而请求参数是字符串,并且客户端不能像传递参数一样传递特性。请求特性只在应用程序内部使用。如果Servlet将Ticket作为请求特性保存在请求中,那么JSP将收到一个Ticket。在请求的生命周期中,应用程序中任何能够访问HttpServletRequest实例的模块都可以访问请求特性。当请求完成时,请求特性将被丢弃。

因为请求特性都是对象,所以在获取它们时必须进行强制转换。

将对象强制转换为Map<Integer, Ticket>是一个未检查操作,所以需要抑制警告。

<%@ page session="false" import="java.util.Map" %>
<%
    @SuppressWarnings("unchecked")
    Map<Integer, Ticket> ticketDatabase =
            (Map<Integer, Ticket>)request.getAttribute("ticketDatabase");
%>
<!DOCTYPE html>
<html>
    <head>
        <title>Customer Support</title>
    </head>
    <body>
        <h2>Tickets</h2>
        <a href="<c:url value="/tickets">
            <c:param name="action" value="create" />
        </c:url>">Create Ticket</a><br /><br />
        <%
            if(ticketDatabase.size() == 0)
            {
                %><i>There are no tickets in the system.</i><%
            }
            else
            {
                for(int id : ticketDatabase.keySet())
                {
                    String idString = Integer.toString(id);
                    Ticket ticket = ticketDatabase.get(id);
                    %>Ticket #<%= idString %>: <a href="<c:url value="/tickets">
                        <c:param name="action" value="view" />
                        <c:param name="ticketId" value="<%= idString %>" />
                    </c:url>"><%= ticket.getSubject() %></a> (customer:
        <%= ticket.getCustomerName() %>)<br /><%
                }
            }
        %>
    </body>
</html>

 

private void listTickets(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
{
    request.setAttribute("ticketDatabase", this.ticketDatabase);

    request.getRequestDispatcher("/WEB-INF/jsp/view/listTickets.jsp").forward(request, response);
}

 

 

关于JSP文档(JSPX)的注意事项

 

 

 

Chapter 4
Updated on 9/4/14.
111.00 KB Click to Download

 

 

 

 

摘录自:[美]Nicholas S.Williams著,王肖峰译 Java Web高级编程 [M]、清华大学出版社,2015、35-62、

posted @ 2016-11-14 22:12  guqiangjs  阅读(255)  评论(0)    收藏  举报