前面总结完了Servlet相关的基础知识, 现在来学习总结一下JSP.
JSP全称Java Server Pages, 是一种动态网页开发技术. JSP与Servlet是一体两面的, 因为JSP网页最终会被容器转译为Servlet源代码, 然后自动编译为.class文件, 再载入.class文件, 最后生成Servlet对象.
下面我们以一个Hello World的例子, 来分析以下JSP和Servlet的关系, 以及JSP的生命周期.
例: 用JSP输出Hello Word, 并分析转译的Servlet代码.
JSP代码如下:
<html>
<head>
<title>Hello JSPtitle>
head>
<boay>
<h1>Hello World!h1>
body>
html>
这就是最简单地JSP的一段代码, 那么, 我们看看它转译成Servlet将会变成什么样子. 转译后的Servlet代码如下(将会略去部分代码, 只保留部分代码):
pubilc final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourseDependent{
//略...
public void _jspInit(){
//略...
}
public void _jspDestroy(){
//略...
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException{
//以下开始为JSP里面的隐式对象
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
//以上为JSP里面的隐式对象
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try{
//略...
out.write("\n");
out.write("\n");
out.write("Hello JSP \n");
out.write("\n");
out.write("\n");
out.write("Hello World!
\n");
out.write("\n");
out.write("\n");
}catch(Throwable t){
//略...
}finally{
//略...
}
}
}
通过上面转译后的Servlet代码可以看到主要有三个重要的方法:_jspInit(), _jspDestroy(), _jspService()这三个方法, 对比Servlet, 我们可以大概猜到这三个方法分别是请求来临时的初始化操作, 请求来临时的善后操作, 请求来临时的处理操作.
那么, 这三个方法时从哪里来的呢? 如果我们想对请求来临的初始化/处理/善后想进行特别的处理是否应该重定义这三个方法?
在上述转译后的Servlet代码中, 我们可以看到, 该转译后的Servlet继承了org.apache.jasper.runtime.HttpJspBase类, 那么我们来大致查看一下HttpJspBase类中都有哪些方法, 他们的关系有时如何, HttpJspBase类代码如下:
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage{
//HttpJspBase是一个抽象类,说明该类的所有方法都应该由其子类来实现.
public final void init(ServletConfig config) throws ServletException{
super.init(config);
jspInit();
_jspInit();
}
//略...
public final void destroy(){
jspDestroy();
_jspDestroy();
}
public final void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
_jspService(request, response);
}
public void jspInit(){}
public void _jspInit(){}
public void jspDestroy(){}
public void _jspDestroy(){}
public abstract void _jspService(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
通过上述代码可以看出Servlet的init()/destroy()/service()里面都调用了哪些方法.
init(): 其中调用了jspInit()和_jspInit().
destroy(): 其中调用了jspDestroy()和_jspDestroy().
service(): 其中调用了_jspService(), JSP转译为Servlet后, 其中所定义的核心代码都在_jspService()中.
例: 将下面的Servlet代码转换为JSP代码
Servlet代码如下:
@WebServlet("/hello.view")
public class FirstServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter(); String name = request.getParameter("name");
out.println("");
out.println("");
out.println("hello Servlet ");
out.println("");
out.println("");
out.println(" hello!"
+ name + "!");
out.println("");
out.println("");
out.close();
}
}
转译成JSP代码如下:
<%@page contentType = "text/html" pageEncoding = "UTF-8"%>
<html>
<head>
<title>hello servlettitle>
head>
<body>
<%
String name = request.getParameter("name");
%>
<h1>hello! <%= name%>h1>
body>
html>
什么是指示元素?
指示元素是在容器将JSP转译成Servlet代码的时候, 声明的一些必须遵守的信息. 在JSP中, 主要由三种常用的指示类型: page, include, taglib.
page指示类型使用示例:
<%@page import = "java.util.Date" %>
<%@page contentType = "text/html pageEncoding = UTF-8" %>
...
上述代码中的import/contentType/pageEncoding都是page指示元素的属性.
include指示类型使用示例:
假设有如下代码: includeDemo.jsp
<%@ page contentType="text/html" pageEncoding="UTF-8" language="java" %>
<%@include file="header.jspf"%>
<h1>include 示范主体h1>
<%@include file="foot.jspf" %>
header.jspf代码如下(.jspf文件是被.jsp文件include的文件, 本质上就是.jsp文件):
<html>
<head>
<%@page pageEncoding="UTF-8" %>
<title>include 示范开头title>
head>
<body>
foot.jspf代码如下:
<%@page pageEncoding="UTF-8" %>
body>
html>
在上述includeDemo.jsp进行转译时, 会将header.jspf和foot.jspf包含进来进行转译, 也就是说将<%@include file = “header.jspf” %>和<%@include file = “foot.jspf” %>分别用header.jspf和foot.jspf文件进行替换.
include指示元素包含进来的JSP们, 最终只会转译生成一个Servlet文件, 而后面提到的的网页则会生成一个个独立的Servlet.
声明元素的语法为: <%! 类成员声明或方法声明%>, 示例如下:
<%!
String name = "dela";
String passwd = "123456";
boolean checkUser(String name, String passwd){
return this.name.equals(name) && this.passwd.equals(passwd);
}
%>
上述代码转译后在Servlet中会生成如下代码:
pubilc final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourseDependent{
String name = "dela";
String passwd = "123456";
boolean checkUser(String name, String passwd){
return this.name.equals(name) && this.passwd.equals(passwd);
}
//略...
}
你可能会想, 这样声明和直接写Java代码(即后面提到的Scriptlet元素)有什么区别, 不都是转译到Servlet中了吗?
当然不是, 声明元素中所写的Java代码将会生成HttpJspBase类中像init()/destroy()/service()这样的方法成员或数据成员. 而直接写的Java代码, 都将转译到_jspService()方法中.
声明元素的作用之一就是前面提到的: 我们想自定义JSP的一些初始和结尾工作时, 可以在声明元素中重定义jspInit()和jspDestroy()方法.
Scriptlet元素就是前面提到的直接在JSP中编写Java代码, 格式为: <% Java语句 %>. 其示例前面都有提到过, 在此不过多赘述.
表达式元素就是前面提到的赋值语句, 格式为<%=Java表达式 %>.其示例前面也都有提到过, 在此不过多赘述.
注释元素就是在JSP中的注释, 格式为:< !– 注释 – > .
简单的讲, 就是将Servlet里面一些常用的对象设置为JSP里面的隐式对象. 比如, JSP中的out就是Servlet里面的PrintWriter对象, request就是HttpServletRequest对象. 隐式对象的存在就是为了简化Java代码, 我们不必要再通过getxxxx()来取得这些常用的对象.
JSP隐式对象的说明如下:
- out: 转译后对应JspWriter对象, 其内部关联一个PrintWriter对象.
- request: 转译后对应HttpServletRequest对象.
- response: 转译后对应HttpServletResponse对象.
- config: 转译后对应ServletConfig对象.
- application: 转译后对应ServletContext对象.
- session: 转译后对应HttpSession对象.
- pageContext: 转译后对应pageContext对象, 它提供了JSP页面资源的封装, 并可设置页面范围属性.
- exception: 转译后对应Throwable对象, 代表由其他JSP页面抛出的异常对象, 只会出现于JSP错误页面(isErrorPage属性设置为true的JSP页面).
- page: 转译后对应this.
需要注意的是: 隐式对象只能在<%%>与<%=%>之间使用, 因为隐式对象为_jspService()里面的局部变量, 无法在其他成员方法中使用.
在JSP中, 可用page指示元素的errorPage属性来指定错误处理页面来进行错误处理. 如下:
<%@page errorPage = "error.jsp" %>
然后在error.jsp中将page元素的isError属性设置为true. 随后在error.jsp中可通过打印隐式对象exception的错误信息(如异常堆栈信息), 以便后续处理.