1、当客户第一次访问Jsp页面时,容器主要按以下流程处理JSP文件。大体流程如下图所示。
图1 JSP总体执行流程图
2、执行原理及流程
假设:容器=具体的Jsp引擎(Tomcat中的JSP引擎就是一个Servlet程序,它负责解释和执行JSP页面)
1)客户通过浏览器向服务器端的JSP页面发送请求。
2)容器接受到客户请求后,会检查JSP文件对应编译后的Servlet文件(.class)是否存在。如果不存在,则跳转到第4)步,否则执行下一步。
3)容器检查JSP页面是否有更新(修改),没有更新,则跳转到第5)步,否则执行下一步。
4)容器将JSP文件转换为Servlet类源文件(.java)。(此步会检查和捕获JSP语法错误)
5)容器将Servlet源文件(.java)编译成相应的字节码(.class)文件。(会检查和捕获Java语法错误)
6)容器将字节码(.class)文件加载到内存。
7)容器实例化Servlet(调用构造函数),然后调用初始化方法(jspInit())初始化Servlet。到此为止,Servlet对象真正成为一个Servlet,准备就绪,可以处理客户的请求了。
8)容器创建一个新的线程来运行Servlet并运行Servlet的_jspService()方法处理客户的请求。
9)Servlet生成并向客户返回一个响应(或把请求转发给另一个Web应用组件处理)。
3、流程补充说明
1)按照容器处理Jsp的流程,通常可以大体分为两个时期:转译时期(Translate Time)和请求时期(Request Time)。
a)转译时期:将Jsp文件转换成Servlet类 。
b)请求时期:使用Servlet处理请求并把响应返回客户端。
而转译时期具体包含转换和编译工作
a)转换:将JSP文件转换为Servlet类源文件(.java)。(此步会检查和捕获JSP语法错误)
b)编译:将Servlet源文件(.java)编译成字节码(.class)文件。(会检查和捕获Java语法错误)
2) 容器在转译时期的转换工作主要包括哪些呢?
a)容器(Jsp引擎)查看Jsp指令(page、include及taglib),得到转换时可能需要的信息。
b)容器创建一个HttpServlet子类。生成的Servlet一般继承org.apache.jasper.runtime.HttpJspBase。
c)如果page指令带有import属性,则容器会在类文件的pagekage语句下面插入import语句。在Tomcat5中,pagekage语句为:package org.apache.jsp。
d)如果有声明(<%! %>标志的内容以及容器自己的声明),容器会将声明作为实例变量写到类文件中,通常是类声明下面,service()方法前面。
e)容器建立_jspService()服务方法。所生成Servlet的_jspService()方法会覆盖超类的service()方法,而且通过超类的service()方法调用,由超类的service()方法传入请求和响应参数。于此同时,容器会声明并初始化Jsp中的所有隐式(内置)对象。
f)容器将普通的Html(模板文件)、scriptlet(脚本文件)和表达式放到jsp_Service()方法中,并最终写到PrintWriter响应输出。
实例参考:一个简单的Jsp文件转换为Servlet文件
Jsp源文件:index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ page import="com.linwei.model.*" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP page</title>
</head>
<body>
<%!
int count=0;
public int doubleCount(){
return count*2;
}
%>
The page count is now:<%=++count %>
<br>
doubleCount:<%= doubleCount()%>
<br>
<%
String localVar = "World";
Person p=new Person();
p.setName("Hello");
%>
person.name:<%=p.getName() %>
<br>
localVar:<%=localVar %>
<br>
Today is:<%= new Date()%>
</body>
</html>
编译后形成的Servlet文件:index_jsp.java
package org.apache.jsp; //容器生成的package语句
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;
import com.linwei.model.*; //自定义导入的包
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
//Jsp中定义的全局变量=Servlet类的实例变量
int count=0;
//Jsp中定义的方法=Servlet类的方法
public int doubleCount(){
return count*2;
}
private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
private static java.util.List _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.AnnotationProcessor _jsp_annotationprocessor;
public Object getDependants() {
return _jspx_dependants;
}
//初始化方法
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
}
//销毁方法
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;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
//设置响应内容类型
response.setContentType("text/html;charset=ISO-8859-1");
//实例化各种隐式(内置)对象
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;
//普通的Html模板文件
out.write("\r\n");
out.write("\r\n");
//scriptlet脚本代码(Java语句)
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
out.write("\r\n");
out.write("<html>\r\n");
out.write(" <head>\r\n");
//表达式
out.write(" <base href=\"");
out.print(basePath);
out.write("\">\r\n");
out.write(" <title>My JSP page</title>\r\n");
out.write(" </head>\r\n");
out.write(" <body>\r\n");
out.write(" ");
out.write("\r\n");
out.write(" The page count is now:");
//实例变量
out.print(++count );
out.write("\r\n");
out.write(" <br>\r\n");
out.write(" doubleCount:");
//调用方法
out.print( doubleCount());
out.write("\r\n");
out.write(" <br>\r\n");
out.write(" ");
//局部变量
String localVar = "World";
Person p=new Person();
p.setName("Hello");
out.write("\r\n");
out.write(" person.name:");
out.print(p.getName() );
out.write("\r\n");
out.write(" <br>\r\n");
out.write(" localVar:");
out.print(localVar );
out.write("\r\n");
out.write(" <br>\r\n");
out.write(" Today is:");
out.print( new Date());
out.write("\r\n");
out.write(" </body>\r\n");
out.write("</html>\r\n");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
3)容器转译(转换和编译)Jsp的工作只发生一次,即容器第一次收到客户的Jsp请求时。Jsp转译后就成为一个“Servlet”对象(初始化前还只是一个普通的Java对象)了。一旦Servlet得到加载和初始化,当有新的请求到达时,容器要做的就是创建或分配一个线程来运行service()方法并处理请求,然后返回响应。