在前面,我们已经看到了JSP如何转换为Java代码,可以写Servlet那样写JSP,拥有强大的功能,那么是否应该在JSP中使用Java?一般不建议,JSP中的Java最大的问题就是太强大。Jsp主要用于表现层,也就是用户界面,即view。一个组织良好的项目,UI和后面的实现应该分离,由不同的人员进行编写。UI的程序员甚至可以不使用java代码。
另外对于一个面向对象的编程语言,不应该将所有的东西都放在一个jsp文件中,而是创建不同的类进行不同的处理。
WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录,class/就位于此目录。Jsp功能很强大,因此一般放在WEB-INF/jsp/目录下。
在前面的介绍中,我们需要为每个jsp文件设置其directive,很是麻烦,可以在web.xml中进行统一的设置,例子如下(颜色字体为标注,并非该web.xml文件的内容):
<?xml version="1.0" encoding="UTF-8"?> <web-app …… > <display-name>Hello World Application</display-name> <jsp-config> <!-- 1、jsp-config中可以有多组jsp-property-group,通过url-pattern来指定jsp文件,例如/WEB-INF/jsp/admin/*.jsp。--> <!-- 本例是表示所有的jsp和jspf文件。--> <!-- 2、如果同时匹配servlet-mapping和jsp-property-group的url-pattern,那么jsp-property-group中的优胜。--> <!-- 如果都是jsp-property-group,那么最佳匹配优胜,即/WEB-INF/jsp/admin/*.jsp优胜于*.jsp。如果都一样,则按先后顺序。--> <!-- 3、对于同时匹配不同的group,根据上述优先进行选择,但是<include-prelude>和<include-coda>的内容都会出作为该文件的开始。--> <!-- 即属性是有优先级别,并选择最优匹配的那个;但是include则匹配多少个都加上去。--> <!-- 4、为了避免不必要的失误,应尽量避免匹配多个group的情况。 --> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <url-pattern>*.jspf</url-pattern> <!-- 定义directive的pageEncoding --> <page-encoding>UTF-8</page-encoding> <!-- 之前讨论了是否应该在jsp中加入Java,缺省是运行,即下面的值为false,如果要禁止,scripting-invalid设置为true。--> <!-- 同样的,<el-ignore>是用来禁止expression language,缺省运行使用,即缺省值为false --> <scripting-invalid>false</scripting-invalid> <!-- include-prelude表明在该group所有的jsp的开始都加上base.jspf,我们可以在base.jspf中定义通用的变量,tab库等等。--> <!-- 使用<include-coda>则是将jspf文件加在jsp的后面。我们可以定义header.jspf和footer.jspf作为jsp模板的前后。--> <include-prelude>/WEB-INF/jsp/base.jspf</include-prelude> <!-- 这个设置很有用,在前面我们知道每个directive、declaration、scriplet和其他的jsp tags在HTML中会输出一个空行。--> <!-- 设置true,会删除有关的输出,使得HTML代码简洁。--> <trim-directive-whitespaces>true</trim-directive-whitespaces> <!-- jsp的缺省内容类型是text/html,可以不设置。--> <default-content-type>text/html</default-content-type> </jsp-property-group> </jsp-config> </web-app>
此外<buffer>设置buffer属性,一般不作修改。
<error-undecleared-namespace>设置当tag的namespace无效时是否给出error,缺省是在给出的,即false。
<is-xml>表明匹配JSP的是JSP文档,这将在后面学习。
<deferred-syntax-allowed-as-literal>也将在后面学习(第6章)。
这些设置除了url-pattern外,其它都是可选的,但必须符合以下顺序:<url-pattern>,<el-ignored>,<page-encoding>,<scripting-invalid>,<is-xml>,<include-prelude>,<include-coda>,<deferred-syntax-allowed-as-literal>,<trim-directive-whitespace>,<default- content-type>,<buffer>, <error-on-undeclared-namespace>
我们将每个jsp包含的头部分,放到WEB-INF/jsp/中,这里确保浏览器不能直接访问。下面是这个base.jspf的例子:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="cn.wei.flowingflying.customer_support.Ticket, cn.wei.flowingflying.customer_support.Attachment" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
在Eclipse中,虽然集体定义了,但是在编写jsp中,不会自动关联到该jspf,如果我们在jsp文件中使用到了Ticket类,会有报错,但是不会影响运行结果,这点也是挺讨厌的,基本是不可接受的,所以一般不将import放在集体定义中。
定义了import的类和使用JSTL的core tag。下面是webapp/根下面的index.jsp,将其重定向到/tickets
<%@ page session="false" %> <c:redirect url="/tickets" />
c:redirect就是使用了JSTL的core tag进行重定向。第一行要求session为false,否则在响应302会在Set-Cookie的消息头中给出:JSESSIONID,同时在Location中带上jsessionid。
浏览器接下来会向http://localhost:8080/customer-support/tickets;jsessionid=461180630E2AFB62C0C667C5E4814FDC发出请求。因此在重定向中,如果没有必要将jsessionid添加在url中,应该设置session为false。
合适的方式是servlet进行业务逻辑的处理,然后将UI呈现交给Jsp处理。
例子一:servlet进行分析后,输出某个静态jsp页面
private void showTicketForm(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp").forward(request, response); }
ticketForm.jsp是个静态的jsp,放在/WEB-INF/目录下,用户不可以直接读取。
采用request.getRequestDispatcher(path).forward(request,response)的方式不是进行重定向,我们可以看到浏览器没有收到3xx的响应,而URL并没有变化。在进行forward之前或者之后,即使我们试图去写response,也不会出现在HTTP的响应中。
例子二:经过servlet分析后,输出到某个动态jsp页面
也就是我们需要将一些数据传递到jsp上,然后通过jsp呈现。在servlet中,可以通过Attribute进行数据的传递。例子如下:
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; // 在Attribute中以对象方式存放信息,当request处理完,attributes也会被丢弃。 // 用于在程序的不同地方处理同一request时进行信息传递,例如在servlet和JSP之间 request.setAttribute("ticketId", idString); request.setAttribute("ticket", ticket); request.getRequestDispatcher("/WEB-INF/jsp/view/viewTicket.jsp").forward(request, response); }
相应的viewTicket.jsp如下。
<%@ page import="cn.wei.flowingflying.customer_support.Ticket, cn.wei.flowingflying.customer_support.Attachment" %> <%@ page session="false" %> <%-- 原本import属于共同定义,但是jsp上有告警,实在受不了,还是给自引入吧 --%> <% //servlet在分析时,将数据放入Attribute,此处将数据取出 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 index = 0; for(Attachment a: ticket.getAttachments()){ if(index ++ >0) out.print(","); %><%-- 下面是通过JSTL的core tag,构建链接,加入连接中GET的参数,如http://localhost:8080/customer-support/ tickets?action=download&ticketId=1&attachment=readme.txt --%> <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>
通过Attribute,可以传递一些复制的对象,例如Map,下面是例子。由于转换到Map<Integer,Ticket>是一个没有检查的操作,进行了@SupressWarning(“unchecked”)。
<%@ page session="false" import="cn.wei.flowingflying.customer_support.Ticket, java.util.Map" %> <% @SuppressWarnings("unchecked") Map<Integer,Ticket> ticketDatabase = (Map<Integer,Ticket>)request.getAttribute("ticketDatabase"); %>
相关链接: 我的Professional Java for Web Applications相关文章