No

在前面,我们已经看到了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。

Java for Web学习笔记(十五):JSP(5)在JSP中使用Java吗?_Java

浏览器接下来会向http://localhost:8080/customer-support/tickets;jsessionid=461180630E2AFB62C0C667C5E4814FDC发出请求。因此在重定向中,如果没有必要将jsessionid添加在url中,应该设置session为false。

将请求从Servlet转到jsp

合适的方式是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相关文章