1. JSP生命周期:
1) JSP即JavaServer Pages的简称,即Java服务器生成页面,见名知意;
2) JSP和Servlet是一体两面,JSP页面最终会被转换成Servlet的Java类;
3) 在J2EE标准下,JSP的生命周期是这样的:
xxx.jsp的源码文件 --转译--> xxx_jsp.java的Servlet源码 --编译--> xxx_jsp.class的类文件 --进入正式的Servlet生命周期--> ...
4) 只有在第一次请求JSP页面时才会发生转译、编译的过程,所以在第一次请求页面时响应会慢很多;
2. xxx.jsp到xxx_jsp.java的转译结果:
1) 伪代码表示大致为:
public final class xxx_jsp extends HttpJspBase { // 略... public void _jspInit() { // JSP语句转译过来的代码,如果JSP代码中定义过初始化初始化方法的话 } public void _jspDestroy() { // JSP语句转译过来的代码,如果JSP代码中定义过资源回收方法的话 } public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 以下每一个数据都对应着JSP的隐式对象,这些变量名都可以直接在JSP代码中使用,相当于一种JSP的环境变量 // 略... PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; // 略... // 接着是加载这些JSP隐式对象,即“JSP的换件变量” // 略... pageContext = 获取pageContext的方法; session = pageContext.getSession(); application = pageContext.getServletContext(); config = pageContext.getServletConfig(); out = pageContext.getOut(); // 略... try { // 略... // JSP转译过来的语句,其中包换重要的画面输出语句 // 略... } catch (Throwable t) { // 略... } finally { // 略... } } }
2) 可以看到三个_jsp方法分别对应着HttpServlet的init、destroy和service方法,只不过该jsp类是直接继承自HttpJspBase类;
3) 其次是命名规则上:如果JSP文件命名为XXX,则转译后的jsp类名称就是XXX_jsp;
i. 看以看到类名_jsp后缀和方法名_jsp前缀相同;
ii. 其实_jsp这个名称在J2EE中是有特殊含义的,是指一个动词,即从JSP代码转译而来;
iii. 所以XXX_jsp就是指从XXX.jsp转译而来的"XXX"Servlet类,而_jspInit就是指从JSP代码中的初始化函数转译而来的init方法的意思;
3. HttpJspBase类:
1) 既然JSP转译类是直接继承自HttpJspBase类,既然其中含有类init、destroy和service方法那么该类必然继承自HttpServlet类;
2) 看一下HttpJspBase类源码的大致结构:
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage { // 略... public void jspInit() {} public void _jspInit() {} public final void init(ServletConfig config) throws ServletException { super.init(config); jspInit(); _jspInit(); } // 略... public void jspDestroy() {} public void _jspDestroy() {} public final void destroy() { jspDestroy(); _jspDestroy(); } public abstract void _jspService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; public final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { _jspService(request, response); } }3) 可以看到这是一个纯虚类,除了init(config: ServletConfig)、destroy、service三个方法是事先定义好的,基本不能改变,其余以jsp和_jsp打头的都是空的,都是留给今后要派生的类来实现的;
4) _jsp和jsp方法:
i. 前面讲过了,_jsp前缀的方法都是经过JSP代码转译而来的,所以在派生类xxx_jsp中这些_jsp方法的内容都是转译而来的,都是“已经存在”的;
ii. jsp方法才是真正的空方法,在派生类中仍然是空的,这些方法是专门用来满足一些特殊需求的,只有在转译后打开xxx_jsp.java源码文件后才能添加,直接在JSP代码中无法实现这些方法;
iii. 可以看到只有init和destroy方法提供了非转译型jsp方法,而service规定必须只能使用转译型_jsp,即服务内容直接用JSP代码实现无需再跑到转译后的Servlet代码中直接用Java语言实现了,这是J2EE的规定,因为JSP就是为了简化service的编写,所以何必要那么麻烦地继续使用Java代码呢?
iv. 从上面的源码中可以看出,都是先调用Java非转译代码再执行JSP代码(转译型代码);
v. 转译型代码(_jsp方法)都是由Web容器来维护的,用户不得修改,只有非转译行代码(jsp方法)用户可以修改;