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<String, String> parameters = new HashMap<String, String>(); /** * 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进行请求处理的.