欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談JSP是如何編譯成servlet并提供服務(wù)的

 更新時間:2021年07月07日 10:27:54   作者:黃智霖-blog  
JSP是Servlet的一種特殊形式,JSP頁面最終是編譯為Servlet執(zhí)行的,那么本文主要介紹了JSP是如何編譯成servlet并提供服務(wù)的,具有一定的參考價值,感興趣的小伙伴們可以參考一下

概述

服務(wù)端對外提供JSP請求服務(wù)的是JspServlet,繼承自HttpServlet。核心服務(wù)入口在service方法,大體流程如下:

  • 首先獲取請求的jspUri,如果客戶端發(fā)起請求:https://xxx.xx.com/jsp/test.jsp,那么獲取到的jspUri為:/jsp/test.jsp
  • 然后查看緩存(Map結(jié)構(gòu))中是否包含該jspUri的JspServletWrapper,如果沒有就需要創(chuàng)建一個JspServletWrapper并且緩存起來,并調(diào)用JspServletWrapper的service方法
  • 如果為development模式,或者首次請求,那么就需要執(zhí)行JspCompilationContext.compile() 方法
  • 在JspCompilationContext.compile方法中,會根據(jù)jsp文件的lastModified判斷文件是否已經(jīng)被更新(out dated),如果被更新過了,就需要刪除之前生成的相關(guān)文件,然后將jspLoader置空(后面需要加載的時候如果jspLoader為空,就會創(chuàng)建一個新的jspLoader),調(diào)用Compiler.compile方法生成servlet,設(shè)置reload為true(默認為true),后面會根據(jù)reload參數(shù)判斷是否需要重新加載該servlet
  • 調(diào)用JspServletWrapper.getServlet方法獲取最終提供服務(wù)的servlet,這個過程會根據(jù)reload參數(shù)看是否需要重載servlet,如果需要重載,那么就會獲取jspLoader實例化一個新的servlet(如果前面發(fā)現(xiàn)jsp文件過期,那么此時獲取的jspLoader為空,則會創(chuàng)建一個新的jspLoader),并且設(shè)置reload為false
  • 調(diào)用servlet的service方法提供服務(wù),如果servlet實現(xiàn)了SingleThreadModel接口,那么會用synchronized做同步控制

源碼分析

首先看JspServlet的核心邏輯,主要是獲取jspUri和獲取JspServletWrapper,分別是入口service方法和serviceJspFile方法,代碼如下(部分):

/**
 * 獲取jspUri,然后調(diào)用serviceJspFile方法
 */
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String jspUri = this.jspFile;
        String pathInfo;
        if (jspUri == null) {
            pathInfo = (String)request.getAttribute(Constants.JSP_FILE);
            if (pathInfo != null) {
                jspUri = pathInfo;
                request.removeAttribute(Constants.JSP_FILE);
            }
        }
 
        if (jspUri == null) {
            jspUri = (String)request.getAttribute("javax.servlet.include.servlet_path");
            if (jspUri != null) {
                pathInfo = (String)request.getAttribute("javax.servlet.include.path_info");
                if (pathInfo != null) {
                    jspUri = jspUri + pathInfo;
                }
            } else {
                jspUri = request.getServletPath();
                pathInfo = request.getPathInfo();
                if (pathInfo != null) {
                    jspUri = jspUri + pathInfo;
                }
            }
        }
 
 
        boolean precompile = this.preCompile(request);
        this.serviceJspFile(request, response, jspUri, precompile);
    }
 
 
/**
 * 主要獲取JspServletWrapper,然后調(diào)用JspServletWrapper.service方法
 */
private void serviceJspFile(HttpServletRequest request, HttpServletResponse response, String jspUri, boolean precompile) throws ServletException, IOException {
        JspServletWrapper wrapper = this.rctxt.getWrapper(jspUri);
        if (wrapper == null) {
            synchronized(this) {
                wrapper = this.rctxt.getWrapper(jspUri);
                if (wrapper == null) {
                    if (null == this.context.getResource(jspUri)) {
                        this.handleMissingResource(request, response, jspUri);
                        return;
                    }
 
                    wrapper = new JspServletWrapper(this.config, this.options, jspUri, this.rctxt);
                    this.rctxt.addWrapper(jspUri, wrapper);
                }
            }
        }
 
        try {
            //核心服務(wù)方法
            wrapper.service(request, response, precompile);
        } catch (FileNotFoundException var8) {
            this.handleMissingResource(request, response, jspUri);
        }
 
    }

然后進入JspServletWrapper.service方法(部分代碼):

//注意這個reload是volatile修飾的
private volatile boolean reload = true;
 
public void service(HttpServletRequest request, HttpServletResponse response, boolean precompile) throws ServletException, IOException, FileNotFoundException {
        Servlet servlet;
        try {
            if (this.ctxt.isRemoved()) {
                throw new FileNotFoundException(this.jspUri);
            }
            //判斷development模式和firstTime(首次請求)
            if (!this.options.getDevelopment() && !this.firstTime) {
                if (this.compileException != null) {
                    throw this.compileException;
                }
            } else {
                synchronized (this) {
                    this.firstTime = false;
                    //調(diào)用JspCompilationContext.compile方法
                    this.ctxt.compile();
                }
            }
            //獲取最終提供服務(wù)的servlet
            servlet = this.getServlet();
            if (precompile) {
                return;
            }
        }
        try {
            //根據(jù)是否實現(xiàn)SingleThreadModel決定是否需要做同步控制
            if (servlet instanceof SingleThreadModel) {
                synchronized (this) {
                    servlet.service(request, response);
                }
            } else {
                servlet.service(request, response);
            }
        }
    }

這里主要看JspCompilationContext.complie方法:

public void compile() throws JasperException, FileNotFoundException {
        this.createCompiler();
        if (this.jspCompiler.isOutDated()) {
            if (this.isRemoved()) {
                throw new FileNotFoundException(this.jspUri);
            }
            try {
                //清楚文件數(shù)據(jù)
                this.jspCompiler.removeGeneratedFiles();
                //置空jspLoader,現(xiàn)在置null,后面就會創(chuàng)建一個新的JspLoader
                this.jspLoader = null;
                //根據(jù)jsp生成servlet的邏輯,實現(xiàn)主要有AntCompiler和JDTCompiler,默認JDTCompiler
                this.jspCompiler.compile();
                //設(shè)置reload為true,后面根據(jù)reload參數(shù)判斷是否需要重新加載
                this.jsw.setReload(true);
                this.jsw.setCompilationException((JasperException) null);
            }
        }
    }

要注意對于isOutDated方法的判斷,并不是簡單地每次請求都檢查jsp文件是否更新,而是有一個間隔時間,如果此次檢查更新的時間在上一次檢查更新+間隔時間之內(nèi),也就是沒有超過間隔時間,那么就不會去檢查jsp文件的更新。這就是我們說的jsp熱更新延時生效,isOutDated是Compiler的方法,如下(部分代碼):

    public boolean isOutDated(boolean checkClass) {
        if (this.jsw != null && this.ctxt.getOptions().getModificationTestInterval() > 0) {
            //getModificationTestInterval就是檢查最短間隔時間,單位為秒
            if (this.jsw.getLastModificationTest() + (long)(this.ctxt.getOptions().getModificationTestInterval() * 1000) > System.currentTimeMillis()) {
                return false;
            }
 
            this.jsw.setLastModificationTest(System.currentTimeMillis());
        }
 
        Long jspRealLastModified = this.ctxt.getLastModified(this.ctxt.getJspFile());
        if (jspRealLastModified < 0L) {
            return true;
        } else {
            long targetLastModified = 0L;
            File targetFile;
            if (checkClass) {
                targetFile = new File(this.ctxt.getClassFileName());
            } else {
                targetFile = new File(this.ctxt.getServletJavaFileName());
            }
 
            if (!targetFile.exists()) {
                return true;
            } else {
                targetLastModified = targetFile.lastModified();
                if (checkClass && this.jsw != null) {
                    this.jsw.setServletClassLastModifiedTime(targetLastModified);
                }
 
                if (targetLastModified != jspRealLastModified) {
                    if (this.log.isDebugEnabled()) {
                        this.log.debug("Compiler: outdated: " + targetFile + " " + targetLastModified);
                    }
 
                    return true;
                } else if (this.jsw == null) {
                    return false;
                } 
            }
        }

另外,這里還涉及到JSP的編譯工作,編譯工作主要由org.apache.jasper.compiler.Compiler編譯器負責,Compiler是一個抽象類,apache-jsp中提供了兩種實現(xiàn):AntCompilerJDTCompiler,默認使用的編譯器為JDTCompiler。

最后回到JspServletWrapper.getServlet方法:

private volatile boolean reload = true;
public Servlet getServlet() throws ServletException {
        //reload是被volatile修飾的一個boolean變量
        //這里進行雙重檢測
        if (this.reload) {
            synchronized (this) {
                if (this.reload) {
                    //需要重載
                    this.destroy();
                    Servlet servlet;
                    try {
                        InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(this.config);
                        //創(chuàng)建一個新的serlvet實例對象,注意這里的getJspLoader方法
                        servlet = (Servlet) instanceManager.newInstance(this.ctxt.getFQCN(), this.ctxt.getJspLoader());
                    } catch (Exception var6) {
                        Throwable t = ExceptionUtils.unwrapInvocationTargetException(var6);
                        ExceptionUtils.handleThrowable(t);
                        throw new JasperException(t);
                    }
 
                    servlet.init(this.config);
                    if (!this.firstTime) {
                        this.ctxt.getRuntimeContext().incrementJspReloadCount();
                    }
                    this.theServlet = servlet;
                    this.reload = false;
                }
            }
        }
        return this.theServlet;
    }

可以看到,方法中使用了雙重檢測機制判斷是否需要重載,reload參數(shù)由volatile修飾保證可見性。在創(chuàng)建新的servlet實例的時候,classLoader是通過JspCompilationContext.getJspLoader方法獲取的,看看這個方法的邏輯:

public ClassLoader getJspLoader() {
        if (this.jspLoader == null) {
            this.jspLoader = new JasperLoader(new URL[]{this.baseUrl}, this.getClassLoader(), this.rctxt.getPermissionCollection());
        }
 
        return this.jspLoader;
    }

在前面JspCompilationContext.complie的邏輯中,如果檢測到j(luò)sp文件被更新過(過期),那么jspLoader會被設(shè)置為null,此時就會創(chuàng)建一個新的jspLoader(JasperLoader),然后使用新的loader加載新的servlet,以完成jsp的熱更新,老的classloader在之后會被GC直接回收。

到此這篇關(guān)于淺談JSP是如何編譯成servlet并提供服務(wù)的的文章就介紹到這了,更多相關(guān)JSP編譯成servlet內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot+vue實現(xiàn)Token自動續(xù)期(雙Token方案)

    springboot+vue實現(xiàn)Token自動續(xù)期(雙Token方案)

    雙Token方案通過訪問令牌和刷新令牌提高用戶登錄安全性和體驗,訪問令牌有效期短,包含用戶信息,用于請求校驗,本文就來介紹一下springboot+vue實現(xiàn)Token自動續(xù)期(雙Token方案),感興趣的可以了解一下
    2024-10-10
  • Java使用Thumbnailator實現(xiàn)圖片處理功能

    Java使用Thumbnailator實現(xiàn)圖片處理功能

    Thumbnailator是一個簡單且功能強大的Java庫,用于生成縮略圖和執(zhí)行其他圖片處理任務(wù),在這篇博客中,我們將介紹如何使用Thumbnailator進行圖片的縮放、裁剪、旋轉(zhuǎn)等操作,需要的朋友可以參考下
    2024-07-07
  • Java中的線程同步與ThreadLocal無鎖化線程封閉實現(xiàn)

    Java中的線程同步與ThreadLocal無鎖化線程封閉實現(xiàn)

    這篇文章主要介紹了Java中的線程同步與ThreadLocal無鎖化線程封閉實現(xiàn),Synchronized關(guān)鍵字與ThreadLocal變量的使用是Java中線程控制的基礎(chǔ),需要的朋友可以參考下
    2016-03-03
  • Java線程池隊列PriorityBlockingQueue原理分析

    Java線程池隊列PriorityBlockingQueue原理分析

    這篇文章主要介紹了Java線程池隊列PriorityBlockingQueue原理分析,PriorityBlockingQueue隊列是?JDK1.5?的時候出來的一個阻塞隊列,但是該隊列入隊的時候是不會阻塞的,永遠會加到隊尾,需要的朋友可以參考下
    2023-12-12
  • spring學(xué)習(xí)之util:properties的使用

    spring學(xué)習(xí)之util:properties的使用

    這篇文章主要介紹了spring學(xué)習(xí)之util:properties的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Java數(shù)據(jù)結(jié)構(gòu)之集合框架與常用算法詳解

    Java數(shù)據(jù)結(jié)構(gòu)之集合框架與常用算法詳解

    Java集合框架是Java中常用的數(shù)據(jù)結(jié)構(gòu)庫,包括List、Set、Map等多種數(shù)據(jù)結(jié)構(gòu),支持快速的元素添加、刪除、查找等操作,可以用于解決各種實際問題。Java中也有多種常用算法,如排序、查找、遞歸等,在數(shù)據(jù)處理和分析中有廣泛應(yīng)用
    2023-04-04
  • SpringBoot中配置文件及切換方式

    SpringBoot中配置文件及切換方式

    這篇文章主要介紹了SpringBoot中配置文件及切換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • java swing 實現(xiàn)加載自定義的字體

    java swing 實現(xiàn)加載自定義的字體

    這篇文章主要介紹了java swing 實現(xiàn)加載自定義的字體,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java ThreadLocal的使用詳解

    Java ThreadLocal的使用詳解

    ThreadLocal是線程私有的局部變量存儲容器,可以理解成每個線程都有自己專屬的存儲容器,用來存儲線程私有變量。ThreadLocal 在日常開發(fā)框架中應(yīng)用廣泛,但用不好也會出現(xiàn)各種問題,本文就此講解一下。
    2021-05-05
  • Spring Data + Thymeleaf 3 + Bootstrap 4 實現(xiàn)分頁器實例代碼

    Spring Data + Thymeleaf 3 + Bo

    本篇文章主要介紹了Spring Data + Thymeleaf 3 + Bootstrap 4 實現(xiàn)分頁器實例代碼,非常具有實用價值,需要的朋友可以參考下
    2017-05-05

最新評論