Java Web 高级编程 - 第四章 使用JSP显式页面内容
本章内容
- 使用<br/>替代output.println("<br/>")
- 创建第一个JSP
- 在JSP中使用Java(以及不使用Java的原因)
- 结合使用Servlet和JSP
- 关于JSP文档(JSPX)的注意事项
使用<br/>替代output.println("<br/>")
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>标签
JavaServer Pages Standard Tag Library 1.1 Tag Reference
<%@ 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代码的经验,因此为它们提供这样的能力是很危险的。
在一个具有良好结构。干净代码的应用程序中,表示层通常应该与业务层分开,同样也与数据持久层分隔开。
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属性
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); }
| Chapter 4 Updated on 9/4/14. |
111.00 KB | Click to Download |
摘录自:[美]Nicholas S.Williams著,王肖峰译 Java Web高级编程 [M]、清华大学出版社,2015、35-62、

浙公网安备 33010602011771号