key 发表于 10-7-2009 13:33:25

JSP的一些小知识 —— 小key出品

本文的目的是介绍/总结一些JSP开发时的一些小知识和技巧。
都是一些很小的东西。

[ 本帖最后由 key 于 10-7-2009 12:42 编辑 ]

key 发表于 10-7-2009 13:40:20

一:快速编译和生成servlet类

我们都知道,JSP的life-cycle有七步,其中前两步是JSP的转译与编译,
后五步是servlet的生命周期的JSP对应版本。

而JSP的转译和编译是比较花时间的。我们会因为下面两个原因需要单独的
编译和生成servlet类,而不想执行它:

1. 快速测试JSP语法
2. 全面编译一次JSP网页,而避免用户第一次点击时响应时间过长

这里可以采用:
http://localhost:8080/<context-path>/<jsp-path>.jsp?jsp_precompile=true

来执行。如果你用linux或cygwin,还可以用:

curl "http://localhost:8080/<context-path>/<jsp-path>.jsp?jsp_precompile=true"

或者:
find <context-path> -name "*.jsp" -exec curl "http://localhost:8080/{}?jsp_precompile=true" \;

[ 本帖最后由 key 于 10-7-2009 12:42 编辑 ]

formatc 发表于 10-7-2009 13:42:30

没抢到沙发,占个板凳先~~

key 发表于 10-7-2009 13:43:26

原帖由 formatc 于 10-7-2009 12:42 发表 http://www.freeoz.org/forum/images/common/back.gif
没抢到沙发,占个板凳先~~

多谢支持

procoder 发表于 10-7-2009 13:49:17

你的兴趣很广泛呀。

key 发表于 10-7-2009 13:50:46

二:查看jsp的implicit variable

参考JSP的书,可以找到关于implicit variable的说明。
关于由JSP生成的Servlet类,我们知道它是implements Servlet, JspPage, HttpJspPage
这些接口(其实这些接口本身是继承关系)。尤其是Servlet接口,
我们必须记住相应的接口方法。

但除此以外,一些implicit variable则不容易记住。在开发的过程中,如果我们一时不记得
这些东西,可以做一件简单的事把这些变量找到:

1. touch empty.jsp,这个文件就是空文件。
2. curl http://localhost:8080/<context-path>/<jsp-path>.jsp?jsp_precompile=true

然后进入work目录下找到empty_jsp.java查看。下面是tomcat 6.0下的一个sample:29   public void _jspService(HttpServletRequest request, HttpServletResponse response)
30         throws java.io.IOException, ServletException {
31
32   PageContext pageContext = null;
33   HttpSession session = null;
34   ServletContext application = null;
35   ServletConfig config = null;
36   JspWriter out = null;
37   Object page = this;
38   JspWriter _jspx_out = null;
39   PageContext _jspx_page_context = null;
40
41
42   try {
43       response.setContentType("text/html");
44       pageContext = _jspxFactory.getPageContext(this, request, response,
45               null, true, 8192, true);
46       _jspx_page_context = pageContext;
47       application = pageContext.getServletContext();
48       config = pageContext.getServletConfig();
49       session = pageContext.getSession();
50       out = pageContext.getOut();
51       _jspx_out = out;
52
53   } catch (Throwable t) {
54       if (!(t instanceof SkipPageException)){
55         out = _jspx_out;
56         if (out != null && out.getBufferSize() != 0)
57         try { out.clearBuffer(); } catch (java.io.IOException e) {}
58         if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
59       }
60   } finally {
61       _jspxFactory.releasePageContext(_jspx_page_context);
62   }
63   }

key 发表于 10-7-2009 14:27:49

三:玩转page指令的几个attributes

通过“二”,我们可以进一步来看看page的几个关键属性,包括:
1. session
2. errorPage
3. isErrorPage
4. buffer
5. autoFlush
6. contentType

1. session
不妨在empty.jsp中加入:<%@ page session="false" %>重新编译,你就会发现empty_jsp.java中的line-3333   HttpSession session = null;和line-49不见了:49       session = pageContext.getSession();2. errorPage
在empty.jsp加入:<%@ page errorPage="error.jsp" %>看到的是line-44 ~ line-45变了:
原来是:44       pageContext = _jspxFactory.getPageContext(this, request, response,
45               null, true, 8192, true);现在是:44       pageContext = _jspxFactory.getPageContext(this, request, response,
45               "error.jsp", true, 8192, true);3. isErrorPage
如果在empty.jsp中加入:<%@ page isErrorPage="true" %>相应地,在empty_jsp.java中会发现多了下面几行东西:    Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);
    if (exception != null) {
      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }这里exception又是一个implicit variable,而它是Throwable类型,这个需要注意。

4. buffer
5. autoFlush

这两个属性是用来改变getPageContext()中的参数的,可以看到tomcat 6.0的缺省
buffer为8kb,和JSP Specification要求的最低值相同。

6. contentType
在empty.jsp中加入:<%@ page contentType="text/html; charset=UTF-8" %>line-43马上转变成:43       response.setContentType("text/html; charset=UTF-8");

key 发表于 10-7-2009 15:30:57

四:<%@include%>与<jsp:include/>有什么不同?

同样是include,前者是一个指令,directive,后者是一个动作,action。
所有的指令,包括page, include都是在translation phase完成的。
而后者相法于RequestDispatcher.include()方法。

测试include directive
其中include_directive_test.jsp的内容如下:1 <% out.println("before including"); %>
2 <%@ include file="include_directive_including.inc" %>
3 <% out.println("after including"); %>而include_directive_including.inc的内容如下:1 <% out.println("including file content"); %>预编译后的java文件内容如下: 57
58out.println("before including");
59       out.write('\n');
60out.println("including file content");
61       out.write('\n');
62       out.write('\n');
63out.println("after including");
64       out.write('\n');显然,转换器把整个including文件嵌入主文件之中,形成一个translation unit。
理解这个translation unit的概念很重要,因为在JSP/Servlet语法中,有不少东西是以translation unit
为单位进行约束的。比如page的所有属性中,除了import外,其他属性最多只能出现 1 次。

可能还有同学注意到,这个预编译的.java文件还有几行很特别的内容: 12   private static java.util.List _jspx_dependants;
13
14   static {
15   _jspx_dependants = new java.util.ArrayList(1);
16   _jspx_dependants.add("/include_directive_including.inc");
17   }这个让servlet container在每次执行更新检查的时候,需要注意把including文件一并进行
检查。

换成<jsp:include />看看
如果换成<jsp:include page="xxx" />的话,得到的结果是这样的:      org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "include_directive_including.inc", out, false);而更深入的了解需要看JspRuntimeLibrary.java的代码。相关部分代码如下: 948   public static void include(ServletRequest request,
949                              ServletResponse response,
950                              String relativePath,
951                              JspWriter out,
952                              boolean flush)
953         throws IOException, ServletException {
954
955         if (flush && !(out instanceof BodyContent))
956             out.flush();
957
965         String resourcePath = getContextRelativePath(request, relativePath);
966         RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
967
968         rd.include(request,
969                  new ServletResponseWrapperInclude(response, out));
970
971   }一句rd.include()就把这司马昭之心暴露无遗了。

key 发表于 10-7-2009 16:02:26

五:4种scope

JSP中数据共享有4个scope(我们一般会说servlet的共享范围有3个)。
这四个范围对应了 8 个隐含对象中的 4 个(正好一半),分别是:

1. application
2. session
3. request
4. pageContext

其中,除了pageContext不可以枚举所有的共享名称外,
其他 3 个都可以用getAttributeNames()进行枚举。

很容易可以看到,前面3个scope和servlet是一致的,
但第4个pageContext就是JSP特有的。这是因为servlet是一个Java类,
JSP虽然说最终还是一个Java Servlet,但毕竟穿着马甲嘛,多少要装模作样一下。
各种scriptlet之间的数据如何进行通信,是一个问题。通过pageContext则可以解决
这个问题。

虽然PageContext本身没有getAttributeNames()方法,但他有更强大的方法。
事实上,你可以通过PageContext很方面地操作指定scope的共享数据。+ getAttributeNamesInScope(int scope) : Enumeration<String>
+ APPLICATION_SCOPE : int
+ SESSION_SCOPE : int
+ REQUEST_SCOPE : int
+ PAGE_SCOPE : int

+ findAttribute(String name) : Object
+ getAttributesScope(String name) : int 这里的 getAttributesScope()我估计是get Attribute's Scope的意思吧

+ getAttribute(String name, int scope) : Object
+ setAttribute(String name, Object obj, int scope) : void
+ removeAttribute(String name, int scope) : void显然,JSP Specification是希望PageContext能千秋万载一桶浆糊

淘淘寳寳 发表于 10-7-2009 20:26:41

虽然看得俺头疼,不过很支持技术型小Key:congra

shenlh 发表于 26-8-2009 21:28:27

我支持

key同学是在做SCWCD讲座呀。
页: [1]
查看完整版本: JSP的一些小知识 —— 小key出品