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

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