4 tomcat容器-wrapper

wrapper是什麼

wrapper是包裝了一個應用實現Servlet類的容器的包裝類,管理了這個Servlet的實例化和,初始化,調用及銷燬。

介紹Wrapper之前我要先說明一下我要過濾掉的幾個比較重要的內容

1、SingleThreadMode

實現了這個類的Servlet,tomcat保證不會有兩個線程同時調用同一個該servlet實例,現在已經棄用,因其有誤導性,導致很多人以爲這個是線程安全的,

wrapper中涉及這一塊的我會去掉

2、Jsp

這塊的處理目前也不會在今天的文章中講解,我也會處理掉

wrapper怎麼工作

我們假設wrapper完全正常啓動,我們根據之前管道的內容,我們需要找到StandardWrapper中的基礎閥StandardWrapperValve的invoke

public final void invoke(Request request, Response response)
        throws IOException, ServletException {

       // 這個變量類型是volatile,這樣子使用在併發編程中是不安全的不是原子操作,在之後的
       //版本中改爲atomicLong,當然這個在這裏不重要,與我們的流程無關
        requestCount++;
        //獲取Wrapper容器
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        //獲取父Context容器
        Context context = (Context) wrapper.getParent();
        

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
                // 獲取Servlet實例,實例化過一次之後,保存在Wrapper的instance中
                servlet = wrapper.allocate();
            }
        } 
        
        // Acknowledge the request
        try {
            response.sendAcknowledgement();
        } 
        
        request.setAttribute
            (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
             ApplicationFilterFactory.REQUEST_INTEGER);
        request.setAttribute
            (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
             requestPathMB);
        // Create the filter chain for this request
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        // 創建過濾器
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);
        // Reset comet flag value after creating the filter chain
        request.setComet(false);

        // 調用過濾器和servlet
        filterChain.doFilter
                            (request.getRequest(), response.getResponse()

        // Release the filter chain (if any) for this request
        if (filterChain != null) {
            if (request.isComet()) {
                // If this is a Comet request, then the same chain will be used for the
                // processing of all subsequent events.
                filterChain.reuse();
            } else {
                filterChain.release();
            }
        }

        // 這裏其實沒有做啥
        try {
            if (servlet != null) {
                wrapper.deallocate(servlet);
            }
        } 
       
    }

這個閥主要做了兩個我們需要關注的重要工作

1、 創建容器實例

2、 調用實例的service,在這裏是放在過濾器中處理的,先執行所有過濾,再調用service。

我們是否應該有個問題,Servlet的實例類並不在tomcat這個系統中,tomcat是怎麼加載的

我們首先想到的是不是反射,我們來看看tomcat是怎麼加載Servlet實現類的

public Servlet allocate() throws ServletException {

        // If we are currently unloading this servlet, throw an exception
        if (unloading)
            throw new ServletException
              (sm.getString("standardWrapper.unloading", getName()));

        boolean newInstance = false;
        
            // Load and initialize our instance if necessary
            if (instance == null) {
                synchronized (this) {
                    if (instance == null) {
                        try {
                            if (log.isDebugEnabled())
                                log.debug("Allocating non-STM instance");

                            //沒有實例化開始實例化
                            instance = loadServlet();
                            // For non-STM, increment here to prevent a race
                            // condition with unload. Bug 43683, test case #3
                            if (!singleThreadModel) {
                                newInstance = true;
                                countAllocated.incrementAndGet();
                            }
                        } catch (ServletException e) {
                            throw e;
                        } catch (Throwable e) {
                            throw new ServletException
                                (sm.getString("standardWrapper.allocate"), e);
                        }
                    }
                }
            }
            //如果已經實例化,返回已經實例化的類
                if (!newInstance) {
                    countAllocated.incrementAndGet();
                }
                return (instance);
            }
        }

    }

public synchronized Servlet loadServlet() throws ServletException {

        // Nothing to do if we already have an instance or an instance pool
        if (!singleThreadModel && (instance != null))
            return instance;

        PrintStream out = System.out;
        if (swallowOutput) {
            SystemLogHandler.startCapture();
        }

        Servlet servlet;
        try {
            // 獲取容器中servletClass全限定名
            String actualClass = servletClass;
          // 獲取該容器的加載器,整個容器的加載器會找一篇來講解
            Loader loader = getLoader();
           // 獲取類加載器
            ClassLoader classLoader = loader.getClassLoader();

            Class classClass = null;
            //加載類對象
                    if (classLoader != null) {
                        classClass = classLoader.loadClass(actualClass);
                    } else {
                        classClass = Class.forName(actualClass);
                    }
            //實例化servlet實例
            
            servlet = (Servlet) classClass.newInstance();
            // 第一次創建servlet的時候會執行一次init,所以servlet的初始化僅執行一次
            servlet.init(facade);
                
        return servlet;

    }

這塊代碼我們知道爲什麼servlet的init爲什麼只執行一次,也就是servlet的類實例化之後是保存在wrapper容器中的。

也瞭解到我們tomcat的一個Servlet類,是單例的,所以存在併發安全問題。

這裏基本上就是wrapper容器的功能了,下一篇講解的容器是context

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章