前面总结完了Servlet相关的基础知识, 现在来学习总结一下JSP.
1. 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 JSP</title>
</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("<html>\n");
out.write("<head>\n");
out.write("<title>Hello JSP</title>\n");
out.write("</head>\n");
out.write("<body>\n");
out.write("<h1>Hello World!</h1>\n");
out.write("</body>\n");
out.write("</html>\n");
}catch(Throwable t){
//略...
}finally{
//略...
}
}
}
JSP的生命周期
通过上面转译后的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().
- _jspInit()是由转译后的Servlet进行自动重定义.
- 如果想自己设置初始化的操作, 可以重定义jspInit().
destroy(): 其中调用了jspDestroy()和_jspDestroy().
- _jspDestroy()是由转译后的Servlet进行自动重定义.
- 如果想自己设置善后的操作, 可以重定义jspDestroy().
service(): 其中调用了_jspService(), JSP转译为Servlet后, 其中所定义的核心代码都在_jspService()中.
2. Servlet到JSP的转换
例: 将下面的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("<html>");
out.println("<head>");
out.println("<title>hello Servlet</title>");
out.println("<head>");
out.println("<body>");
out.println("<h1> hello!" + name + "!</h1>");
out.println("</body>");
out.println("</html>");
out.close();
}
}
转译成JSP代码如下:
<%@page contentType = "text/html" pageEncoding = "UTF-8"%>
<html>
<head>
<title>hello servlet</title>
</head>
<body>
<%
String name = request.getParameter("name");
%>
<h1>hello! <%= name%></h1>
</body>
</html>
JSP代码格式总结
- JSP的代码格式与HTML基本类似. 如等这些标签都遵循和HTML一样的格式.
- JSP中穿插Java代码: 写在<%%>中, 如下:
- <% String name = request.getParameter(“name”); %>
- JSP中穿插赋值语句: 写在<%=%>中, 如下:
- <h1> hello!” + name + “!</h1> 转化为JSP代码为:<h1> hello! <%= name%></h1>
- JSP中指示元素的格式: <%@指示元素 [属性=”值”]* %>, 如下:
- <%@page contentType = “text/html” pageEncoding = “UTF-8” %>
- 类成员或方法的声明: 写在<%!%>中, 具体示例使用方法后面会详细谈到.
3. 指示元素
什么是指示元素?
指示元素是在容器将JSP转译成Servlet代码的时候, 声明的一些必须遵守的信息. 在JSP中, 主要由三种常用的指示类型: page, include, taglib.
- page: 告知容器如何转译目前的JSP网页.
- include: 告知容器将别的JSP包含进来转译.
- taglib: 告知容器如何转译这个页面的标签库(后面会谈到).
page指示类型
page指示类型使用示例:
<%@page import = "java.util.Date" %>
<%@page contentType = "text/html pageEncoding = UTF-8" %>
...
上述代码中的import/contentType/pageEncoding都是page指示元素的属性.
page指示元素的属性
- import: 告知容器在转译JSP时, 必须在源代码包括的import陈述, 比如需要用到的Java的包等.
- contentType: 告知容器在转译JSP时, 使用的HttpServletRequest的setContentType(), 属性值即为setContentType()方法的属性值.
- pageEncoding: 告知容器这个JSP网页文字的编码, 以及内容类型附加的charset设置.
- 还有一些不常用的属性在此暂不列出.
include指示类型
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.
4. 声明元素 Scriptlet元素 表达式元素 注释元素
声明元素
声明元素的语法为: <%! 类成员声明或方法声明%>, 示例如下:
<%!
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元素
Scriptlet元素就是前面提到的直接在JSP中编写Java代码, 格式为: <% Java语句 %>. 其示例前面都有提到过, 在此不过多赘述.
表达式元素
表达式元素就是前面提到的赋值语句, 格式为<%=Java表达式 %>.其示例前面也都有提到过, 在此不过多赘述.
注释元素
注释元素就是在JSP中的注释, 格式为:< !– 注释 – > .
5. 隐式对象
简单的讲, 就是将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()里面的局部变量, 无法在其他成员方法中使用.
6. 错误处理
在JSP中, 可用page指示元素的errorPage属性来指定错误处理页面来进行错误处理. 如下:
<%@page errorPage = "error.jsp" %>
然后在error.jsp中将page元素的isError属性设置为true. 随后在error.jsp中可通过打印隐式对象exception的错误信息(如异常堆栈信息), 以便后续处理.