最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談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編譯器負責(zé),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中的@CacheEvict 注解的實現(xiàn)

    SpringBoot中的@CacheEvict 注解的實現(xiàn)

    本文主要介紹了SpringBoot中的@CacheEvict注解的實現(xiàn),@CacheEvict 注解用于清空緩存,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-03-03
  • 使用EasyExcel實現(xiàn)簡單的Excel表格解析操作

    使用EasyExcel實現(xiàn)簡單的Excel表格解析操作

    這篇文章主要介紹了如何使用EasyExcel完成簡單的表格解析操作,同時實現(xiàn)了大量數(shù)據(jù)情況下數(shù)據(jù)的分次批量入庫,并記錄每條數(shù)據(jù)入庫的狀態(tài),感興趣的可以了解下
    2025-03-03
  • Spring中HandlerAdapter接口源碼解析

    Spring中HandlerAdapter接口源碼解析

    這篇文章主要介紹了Spring中HandlerAdapter接口源碼解析,HandlerAdapter是一個適配器接口類,適配器模式是指兩個不兼容接口之間的橋梁,要想讓一個接口使用另外一個接口的實現(xiàn)中間可以加一層適配器類,需要的朋友可以參考下
    2023-11-11
  • 如何實現(xiàn)Java監(jiān)聽器詳解

    如何實現(xiàn)Java監(jiān)聽器詳解

    今天帶大家了解Java監(jiān)聽器是如何實現(xiàn)的及實現(xiàn)原理是什么,文中有非常詳細的說明,對正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • SpringBoot對Filter過濾器中的異常進行全局處理方案詳解

    SpringBoot對Filter過濾器中的異常進行全局處理方案詳解

    這篇文章主要介紹了SpringBoot對Filter過濾器中的異常進行全局處理,在SpringBoot中我們通過 @ControllerAdvice 注解和 @ExceptionHandler注解注冊了全局異常處理器,需要的朋友可以參考下
    2023-09-09
  • Java詳解表格的創(chuàng)建與使用流程

    Java詳解表格的創(chuàng)建與使用流程

    這篇文章主要介紹了怎么用Java來創(chuàng)建和使用表格,表格是我們經(jīng)常要用的工具,但是你有想過自己怎么去實現(xiàn)它嗎,感興趣的朋友跟隨文章往下看看吧
    2022-04-04
  • java實現(xiàn)emqx設(shè)備上下線監(jiān)聽詳解

    java實現(xiàn)emqx設(shè)備上下線監(jiān)聽詳解

    這篇文章主要為大家介紹了java實現(xiàn)emqx設(shè)備上下線監(jiān)聽詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • 詳解SpringBoot是如何保證接口安全的

    詳解SpringBoot是如何保證接口安全的

    對于互聯(lián)網(wǎng)來說,只要你系統(tǒng)的接口會暴露在外網(wǎng),就避免不了接口安全問題。?如果你的接口在外網(wǎng)裸奔,只要讓黑客知道接口的地址和參數(shù)就可以調(diào)用,那簡直就是災(zāi)難。這篇文章主要介紹了SpringBoot保證接口安全的方法,需要的可以參考一下
    2023-02-02
  • 在Filter中不能注入bean的問題及解決

    在Filter中不能注入bean的問題及解決

    這篇文章主要介紹了在Filter中不能注入bean的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Maven的配置文件pom.xml詳解(含常用plugin)

    Maven的配置文件pom.xml詳解(含常用plugin)

    pom.xml是Maven項目的核心配置文件,它是 項目對象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項目,感興趣的朋友跟隨小編一起看看吧
    2024-08-08

最新評論

三穗县| 泽州县| 普安县| 濮阳市| 什邡市| 景德镇市| 通榆县| 安溪县| 木兰县| 临西县| 平潭县| 彰化县| 广元市| 丹阳市| 岳普湖县| 武穴市| 黄陵县| 富阳市| 兴安盟| 姚安县| 拜泉县| 浙江省| 富源县| 巴林左旗| 锡林浩特市| 临泽县| 琼结县| 合水县| 额尔古纳市| 凯里市| 平原县| 闽清县| 拜城县| 衡阳市| 集贤县| 石渠县| 莱西市| 夏津县| 鄂托克前旗| 沁源县| 灵璧县|