国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 淺談JSP是如何編譯成servlet并提供服務的

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

2021-09-29 01:01黃智霖-blog Java教程

JSP是Servlet的一種特殊形式,JSP頁面最終是編譯為Servlet執行的,那么本文主要介紹了JSP是如何編譯成servlet并提供服務的,具有一定的參考價值,感興趣的小伙伴們可以參考一下

概述

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

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

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

源碼分析

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * 獲取jspUri,然后調用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,然后調用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 {
            //核心服務方法
            wrapper.service(request, response, precompile);
        } catch (FileNotFoundException var8) {
            this.handleMissingResource(request, response, jspUri);
        }
 
    }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//注意這個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;
                    //調用JspCompilationContext.compile方法
                    this.ctxt.compile();
                }
            }
            //獲取最終提供服務的servlet
            servlet = this.getServlet();
            if (precompile) {
                return;
            }
        }
        try {
            //根據是否實現SingleThreadModel決定是否需要做同步控制
            if (servlet instanceof SingleThreadModel) {
                synchronized (this) {
                    servlet.service(request, response);
                }
            } else {
                servlet.service(request, response);
            }
        }
    }

這里主要看JspCompilationContext.complie方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void compile() throws JasperException, FileNotFoundException {
        this.createCompiler();
        if (this.jspCompiler.isOutDated()) {
            if (this.isRemoved()) {
                throw new FileNotFoundException(this.jspUri);
            }
            try {
                //清楚文件數據
                this.jspCompiler.removeGeneratedFiles();
                //置空jspLoader,現在置null,后面就會創建一個新的JspLoader
                this.jspLoader = null;
                //根據jsp生成servlet的邏輯,實現主要有AntCompiler和JDTCompiler,默認JDTCompiler
                this.jspCompiler.compile();
                //設置reload為true,后面根據reload參數判斷是否需要重新加載
                this.jsw.setReload(true);
                this.jsw.setCompilationException((JasperException) null);
            }
        }
    }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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中提供了兩種實現:AntCompilerJDTCompiler,默認使用的編譯器為JDTCompiler。

最后回到JspServletWrapper.getServlet方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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);
                        //創建一個新的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參數由volatile修飾保證可見性。在創建新的servlet實例的時候,classLoader是通過JspCompilationContext.getJspLoader方法獲取的,看看這個方法的邏輯:

?
1
2
3
4
5
6
7
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的邏輯中,如果檢測到jsp文件被更新過(過期),那么jspLoader會被設置為null,此時就會創建一個新的jspLoader(JasperLoader),然后使用新的loader加載新的servlet,以完成jsp的熱更新,老的classloader在之后會被GC直接回收。

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

原文鏈接:https://blog.csdn.net/huangzhilin2015/article/details/114893129

延伸 · 閱讀

精彩推薦
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 亚洲情视频| 久久精品免费观看 | 亚洲国产aⅴ成人精品无吗 黄色免费在线看 | 中文字幕av一区二区三区 | 欧美另类国产 | 精品一二三区在线观看 | 国产精品1区2区3区 久久免费一区 | 精品国产乱码久久久久久久软件 | 久久久女女女女999久久 | 四虎影视4hu4虎成人 | 中文日韩在线 | 亚洲一本 | 亚洲一区二区在线 | 综合网日韩 | a在线免费观看 | 午夜精品成人一区二区 | 亚洲va欧美va人人爽成人影院 | 国产午夜精品福利 | 日韩操操 | 91av国产精品 | 久久久一区二区 | 亚洲 中文 欧美 日韩 在线观看 | 国内精品在线视频 | 99在线精品视频 | 五月天婷婷国产精品 | 日本一区二区高清不卡 | 天堂中文资源在线 | 91网页版 | 日韩国产欧美视频 | 久久99视频精品 | 日韩视频在线观看 | 久久成人久久爱 | 久久综合成人精品亚洲另类欧美 | 伊人网网站 | 午夜国产精品成人 | 国产精品久久久久久久久久新婚 | 中文字幕,久热精品,视频在线 | 成人看片免费 | 久久久久国产精品 | 午夜精品美女久久久久av福利 | 日日干日日爽 |