浅析Tomcat之StandardWrapper

Wrapper是Context的子容器,它代表了在应用部署描述中的一个单独的servlet.它通过Servlet的init和destroy方法掌管了底层的Servlet的生命周期.并且其中的阀还负责调用Servlet响应用户请求的功能.Wrapper的默认实现是StandardWrapper.

首先,还是很老套地说StandardWrapper的构造函数还是跟StandardContext等类似.也是设置了阀.与它的上层容器不同的是,StandardWrapper已经没有下层容器了,所以在它的addChild方法实现上是直接抛出一个IllegalStateException异常.下面看看几个重要的属性.

/** 
 * The load-on-startup order value (negative value means load on 
 * first call) for this servlet. 
 */  
protected int loadOnStartup = -1;  
  
/** 
 * The initialization parameters for this servlet, keyed by 
 * parameter name. 
 */  
protected HashMap parameters = new HashMap();  
  
/** 
 * Does this servlet implement the SingleThreadModel interface? 
 */  
protected volatile boolean singleThreadModel = false;

loadOnStartup代表的是在Servlet在容器启动的时候的加载顺序,如果为负数的话就是Servlet在第一次被使用的时候进行加载实例化.parameters 应该很熟悉,它代表是是Servlet的参数,这是一个Map.也就是web.xml中所配置的启动参数.singleThreadModel代表是单线程模式,是保证一个特定 servlet 实例的 service 方法在一个时刻仅能被一个线程执行,此保证仅适用于每一个 servlet 实例,因此容器可以选择池化这些对象.

借着loadOnStartup属性我们串一串几个Wrapper中初始化中两个重要的方法.它们是load和loadServlet.那么loadOnStartup在哪里被使用呢?这个可以追溯到Context里面,在Context生命周期的start中调用了它自己的一个函数loadOnStartup,该方法的注释为Load and initialize all servlets marked “load on startup” in the web application deployment descriptor.

TreeMap<Integer, ArrayList<Wrapper>> map =new TreeMap<Integer, ArrayList<Wrapper>>();  
for (int i = 0; i < children.length; i++) {  
    Wrapper wrapper = (Wrapper) children[i];  
    int loadOnStartup = wrapper.getLoadOnStartup();  
    if (loadOnStartup < 0)  
        continue;  
    Integer key = Integer.valueOf(loadOnStartup);  
    ArrayList<Wrapper> list = map.get(key);  
    if (list == null) {  
        list = new ArrayList<Wrapper>();  
        map.put(key, list);  
    }  
    list.add(wrapper);  
}  
  
// Load the collected "load on startup" servlets  
for (ArrayList<Wrapper> list : map.values()) {  
    for (Wrapper wrapper : list) {  
        try {  
            wrapper.load();  
        } catch (ServletException e) {  
            getLogger().error(sm.getString("standardWrapper.loadException",  
                              getName()), StandardWrapper.getRootCause(e));  
            // NOTE: load errors (including a servlet that throws  
            // UnavailableException from tht init() method) are NOT  
            // fatal to application startup  
        }  
    }  
}  

上述代码的上半段是根据Wrapper的loadOnStartup属性对Wrapper进行处理,大于0的进行分类.下半段对已分类好的Wrapper进行便利调用其load方法.TreeMap已经根据其key从小到大进行排序.那么看看Wrapper得load方法.

public synchronized void load() throws ServletException {  
    instance = loadServlet();  
  
    if (!instanceInitialized) {  
        initServlet(instance);  
    }  
  
    if (isJspServlet) {  
        StringBuilder oname =  
            new StringBuilder(MBeanUtils.getDomain(getParent()));  
  
        oname.append(":type=JspMonitor,name=");  
        oname.append(getName());  
  
        oname.append(getWebModuleKeyProperties());  
  
        try {  
            jspMonitorON = new ObjectName(oname.toString());  
            Registry.getRegistry(null, null)  
                .registerComponent(instance, jspMonitorON, null);  
        } catch( Exception ex ) {  
            log.info("Error registering JSP monitoring with jmx " +  
                     instance);  
        }  
    }  
}  

从上述代码中可以清晰地看出,最开始是调用了loadServlet方法加载Servlet,接着判断是否该Servlet已经初始化,如果没有的话则调用init方法进行初始化.Servlet是一个接口,具体的init方法在其子类中实现.后面基本是一些JMX的注册.我们看看loadServlet.

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 {  
        long t1=System.currentTimeMillis();  
        // Complain if no servlet class has been specified  
        if (servletClass == null) {  
            unavailable(null);  
            throw new ServletException  
                (sm.getString("standardWrapper.notClass", getName()));  
        }  
  
        InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();  
        try {  
            servlet = (Servlet) instanceManager.newInstance(servletClass);  
        } catch (ClassCastException e) {  
            unavailable(null);  
            // Restore the context ClassLoader  
            throw new ServletException  
                (sm.getString("standardWrapper.notServlet", servletClass), e);  
        } catch (Throwable e) {  
            e = ExceptionUtils.unwrapInvocationTargetException(e);  
            ExceptionUtils.handleThrowable(e);  
            unavailable(null);  
  
            // Added extra log statement for Bugzilla 36630:  
            // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630  
            if(log.isDebugEnabled()) {  
                log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);  
            }  
  
            // Restore the context ClassLoader  
            throw new ServletException  
                (sm.getString("standardWrapper.instantiate", servletClass), e);  
        }  
  
        if (multipartConfigElement == null) {  
            MultipartConfig annotation =  
                    servlet.getClass().getAnnotation(MultipartConfig.class);  
            if (annotation != null) {  
                multipartConfigElement =  
                        new MultipartConfigElement(annotation);  
            }  
        }  
  
        processServletSecurityAnnotation(servlet.getClass());  
  
        // Special handling for ContainerServlet instances  
        if ((servlet instanceof ContainerServlet) &&  
                (isContainerProvidedServlet(servletClass) ||  
                        ((Context) getParent()).getPrivileged() )) {  
            ((ContainerServlet) servlet).setWrapper(this);  
        }  
  
        classLoadTime=(int) (System.currentTimeMillis() -t1);  
  
        if (servlet instanceof SingleThreadModel) {  
            if (instancePool == null) {  
                instancePool = new Stack<Servlet>();  
            }  
            singleThreadModel = true;  
        }  
  
        initServlet(servlet);  
  
        fireContainerEvent("load", this);  
  
        loadTime=System.currentTimeMillis() -t1;  
    } finally {  
        .....  
    }  
    return servlet;  
  
}  

上述代码实现的功能是Servlet的初始化.最主要的是servlet = (Servlet) instanceManager.newInstance(servletClass);它实例化了web.xml中描述的servlet.下面的大部分代码是异常捕获和调用初始化方法.

经过分析,我们知道Wrapper是在Context初始化的时候根据web.xml中的部署描述进行实例化的,Context不仅根据部署描述进行Wrapper的创建,还调用了其load方法进行了其中Servlet的创建以及初始化.Servlet一旦被加载后,便能为web用户服务.后续博文将介绍请求到达Wrapper后,它是怎样使用Servlet进行请求处理的.



留个爪印