前面的文章已经覆盖了Servlet核心知识,由于JSP与Servlet在很大程度上是相通的,因此关于JSP只需要介绍语法以及一些独特的性质。
同样可以参考。介绍JSP
首先,为什么要有JSP?
要回答这个问题,实际上只需要比较HTML、Servlet、JSP的优劣:
HTML文件是静态的,无法返回动态结果。
Servlet可以动态生成HTML页面,但是在代码里写HTML标记实在是不爽。
于是反过来,在传统的HTML文件中加入Java程序片段和JSP标记,就构成了JSP文件。
JSP是怎么工作的?
当容器接收到客户端对JSP文件的请求时,会解析对应的JSP文件,把它翻译成Servlet源文件,接着把Servlet源文件编译成Servlet类,然后再初始化并运行Servlet。
当然,一旦JSP文件已经被编译过,下次运行时只需要找对应的Servlet类就行了。
可见JSP和Servlet其实是一回事,JSP只是一层外衣。其内容中不论是HTML标记还是Java代码,都大部分被编译到了Servlet类的service()方法中。于是接下来就顺便解释了JSP的生命周期:
JSP的生命周期
编译阶段:Servlet容器编译servlet源文件,生成servlet类
初始化阶段:加载与JSP对应的servlet类,创建其实例,并调用它的初始化方法
执行阶段:调用与JSP对应的servlet实例的服务方法
销毁阶段:调用与JSP对应的servlet实例的销毁方法,然后销毁servlet实例
下图给出了后面三个阶段的示意:
其中:编译出来的Servlet类继承自javax.servlet.jsp.HttpJspPage接口,该接口继承了javax.servlet.Servlet接口。jspInit()就对应Servlet的init(), jspDestroy()就对应Servlet的destroy(), _jspService()自不必说。
下面给出一个JSP文件和编译出来的代码,就一目了然了。
helloapp Hello,<%= request.getParameter("username") %>
编译出的Servlet文件:
public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase { ... public void _jspInit() { } public void _jspDestroy() { } public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { final java.lang.String _jspx_method = request.getMethod(); if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD"); return; } // 可以看出JSP的隐含对象是这么来的。 final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null; try { response.setContentType("text/html"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("\r\n"); out.write("\r\n"); out.write("helloapp \r\n"); out.write("\r\n"); out.write("\r\n"); out.write(" Hello,"); out.print( request.getParameter("username") ); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); } catch (java.lang.Throwable t) { if (!(t instanceof javax.servlet.jsp.SkipPageException)){ out = _jspx_out; if (out != null && out.getBufferSize() != 0) try { if (response.isCommitted()) { out.flush(); } else { out.clearBuffer(); } } catch (java.io.IOException e) {} if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); else throw new ServletException(t); } } finally { _jspxFactory.releasePageContext(_jspx_page_context); } }}
JSP的语法
概括地说,JSP文件中除了可以直接包含HTML文本,还可以包含以下内容:
-
JSP指令(Directive)
用来设置和整个JSP页面相关的属性。语法形式为<%@ 指令 属性1="值1" 属性2="值2" %>
。包括三种指令:Page: 可以指定language、content_type、errorPage、session等,还可以用import属性引入包。
Include: 包含其他文件。
Taglib: 指定自定义标签。
-
JSP声明
声明一个或多个变量、方法,供后面的Java代码使用。声明中的变量会被编译成Servlet的(私有)成员变量。示例:
<%! int i = 0; %> <%! int a, b, c; %> <%! Circle a = new Circle(2.0); %>
Java程序片段
任何在"<%"和"%>"标记之间的Java代码。默认会被编译到Servlet类的service()方法中(如果用Page指定了method属性,则会编到你指定的方法中)。如果你想复写jspInit()和jspDestroy()方法,应该在前面的JSP声明中写。Java表达式
语法形式为<%= JAVA表达式 %>
。相当于被编译成out.print(JAVA表达式)
。-
JSP隐含对象
JSP隐含对象是JSP容器为每个页面提供的Java对象,开发者可以直接使用它们而不用显式声明。JSP隐含对象也被称为预定义变量。JSP所支持的九大隐含对象:对象 描述 request HttpServletRequest类的实例 response HttpServletResponse类的实例 out PrintWriter类的实例,用于把结果输出至网页上 session HttpSession类的实例 application ServletContext类的实例,与应用上下文有关 config ServletConfig类的实例 pageContext PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问 page 类似于Java类中的this关键字 Exception Exception类的对象,代表发生错误的JSP页面中对应的异常对象 看起来很犀利,但实际上从前面真实的Servlet代码可以看到,它们不过是在service()方法的最前面预先定义好的局部变量。