Jasper的源码分析

158 阅读1分钟
  • Tomcat在默认的web.xml中配置了org.apache.jasper.servlet.JspServlet,  用于处理所有的  .jsp和  .jspx结尾的请求
  • JspServlet的实现就是运行编译时的入口

当用户发起请求时首先会到达JspServlet.service()。那么我们就从JspServlet.service()方法开始跟踪源码吧。

public void service (HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {
    //获取请求方式
    String method = request.getMethod();
    //校验请求类型
    if (!"GET".equals(method) && !"POST".equals(method) && !"HEAD".equals(method) &&
            !DispatcherType.ERROR.equals(request.getDispatcherType())) {
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
                Localizer.getMessage("jsp.error.servlet.invalid.method"));
        return;
    }

    //获取jsp路径
    String jspUri = jspFile;

    if (jspUri == null) {
        jspUri = (String) request.getAttribute(Constants.JSP_FILE);
    }
    if (jspUri == null) {
        jspUri = (String) request.getAttribute(
                RequestDispatcher.INCLUDE_SERVLET_PATH);
        }
    }

    //判断当前是否是预编译请求
    boolean precompile = preCompile(request);
    //获取JspServletWrapper,并调用 wrapper的service方法。
    serviceJspFile(request, response, jspUri, precompile);

接下来看JspServletWrapper的service方法。

public void service(HttpServletRequest request,
                    HttpServletResponse response,
                    boolean precompile)
        throws ServletException, IOException, FileNotFoundException {

    Servlet servlet;

    try {

        /*
         * 若为开发环境,或者第一次调用则编译
         */
        if (options.getDevelopment() || firstTime ) {
            synchronized (this) {
                firstTime = false;

                // 编译流程
                ctxt.compile();
            }
        } 

    /*
     * (2) 查询加载并实例化编译后的Servlet
     */
     servlet = getServlet();


        /*
         * (3) 更新上次使用时间
         */
        if (unloadAllowed) {
            synchronized(this) {
                if (unloadByCount) {
                    if (unloadHandle == null) {
                        unloadHandle = ctxt.getRuntimeContext().push(this);
                    } else if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
                        ctxt.getRuntimeContext().makeYoungest(unloadHandle);
                        lastUsageTime = System.currentTimeMillis();
                    }
                } else {
                    if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
                        lastUsageTime = System.currentTimeMillis();
                    }
                }
            }
        }
        /*
         * (4) 调用jspServlet执行请求
         */
        if (servlet instanceof SingleThreadModel) {
           synchronized (this) {
               servlet.service(request, response);
            }
        } else {
            servlet.service(request, response);
        }
    }
}

编译流程的源码解析

Jasper的编译流程主要包括代码生成和编译两部分

public void compile() throws JasperException, FileNotFoundException {
    //创建编译器,运行时编译的编译器为JDTCompiler
    createCompiler();
    if (jspCompiler.isOutDated()) {
        jspCompiler.removeGeneratedFiles();
        jspLoader = null;
        //编译jsp文件
        jspCompiler.compile();
        jsw.setReload(true);
        jsw.setCompilationException(null);

    }
}
public void compile(boolean compileClass, boolean jspcMode)
        throws FileNotFoundException, JasperException, Exception {

        //将 jsp 文件编译成一个servlet的 .java 文件。
        String[] smap = generateJava();
        File javaFile = new File(ctxt.getServletJavaFileName());
        Long jspLastModified = ctxt.getLastModified(ctxt.getJspFile());
        javaFile.setLastModified(jspLastModified.longValue());
        //将 servlet 从 .java 文件编译为 .class 文件
        generateClass(smap);
}