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进行请求处理的.