一、解析web.xml
StandardContext.startInternal()启动过程中,触发Lifecycle.CONFIGURE_START_EVENT 事件:
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
通知监听这个事件的监听器,其中包括ContextConfig。ContextConfig中通过configureStart()方法解析web.xml。
ContextConfig类中的webConfig方法完成具体的解析工作:
1、 全局默认配置文件:tomcat目录下conf/web.xml解析为WebXml对象。
2、 具体应用程序下的web.xml解析:例 /examples/WEB-INF/web.xml
3、 扫描应用程序examples下的所有Jar,查找META-INF/web-fragment.xml文件并解析,保存到变量fragments中,格式<String,WebXml>。
4、 已经检索到的WebXml配置进行排序。
5、 基于SPI机制查找ServletContainerInitializer的实现。
6、 扫描/WEB-INF/classes下面的类,处理Annotation:@ WebServlet,@ WebFilter,@ WebListener。代码片段:
String className = clazz.getClassName();
AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
if (annotationsEntries != null) {
for (AnnotationEntry ae : annotationsEntries) {
String type = ae.getAnnotationType();
if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
processAnnotationWebServlet(className, ae, fragment);
}else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
processAnnotationWebFilter(className, ae, fragment);
}else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
fragment.addListener(className);
} else {
// Unknown annotation - ignore
}
}
}
7、扫描jar包中的Annotation:web-fragment.xml所在的jar包。
8、合并配置:所有web-fragment.xml中的配置,合并到应用程序examples中的配置web.xml。
9、合并配置:全局默认配置defaults 合并到应用程序的web.xml。
10、将JSP转换为Servlet。
11、将合并后的最终配置web.xml对象webXml应用到servlet容器(ServletContext),装配servlet上下文;
12、将配置信息保存容器中,供其他组件访问,使得其他组件不需要再次重复上面的步骤去获取配置信息了。
13、查找jar包中的静态资源。
14、将ServletContainerInitializer应用到上下文。
二、根据合并后的web.xml配置装配Servlet上下文
在上面介绍的webConfig()方法中,用合并后的web.xml配置装配Servlet上下文,具体代码为:
webXml.configureContext(context);
进入configureContext方法,按顺序截图代码片段:
1、设置contextParam
for (Entry entry : contextParams.entrySet()) {
context.addParameter(entry.getKey(), entry.getValue());
}
2、filter、listener设置。Listner被保存到Context的变量applicationListeners中。
for (FilterDef filter : filters.values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
for (FilterMap filterMap : filterMaps) {
context.addFilterMap(filterMap);
}
for (JspPropertyGroup jspPropertyGroup : jspPropertyGroups) {
JspPropertyGroupDescriptor descriptor =
new ApplicationJspPropertyGroupDescriptor(jspPropertyGroup);
context.getJspConfigDescriptor().getJspPropertyGroups().add(
descriptor);
}
for (String listener : listeners) {
context.addApplicationListener(listener);
}
3、servlet:servlet都被封装为Wrapper对象。
for (ServletDef servlet : servlets.values()) {
Wrapper wrapper = context.createWrapper();
//…
// jsp-file gets passed to the JSP Servlet as an init-param
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
Map params = servlet.getParameterMap();
for (Entry entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
wrapper.setRunAs(servlet.getRunAs());
Set roleRefs = servlet.getSecurityRoleRefs();
for (SecurityRoleRef roleRef : roleRefs) {
wrapper.addSecurityReference(
roleRef.getName(), roleRef.getLink());
}
wrapper.setServletClass(servlet.getServletClass());
//…
if (servlet.getAsyncSupported() != null) {
wrapper.setAsyncSupported(
servlet.getAsyncSupported().booleanValue());
}
wrapper.setOverridable(servlet.isOverridable());
context.addChild(wrapper);
}
for (Entry entry : servletMappings.entrySet()) {
context.addServletMapping(entry.getKey(), entry.getValue());
}
从上面的代码可以总结至少两点:
1、 配置项加载顺序:上下文参数->Filter->Listner->Servlet。
2、 Servlet容器支持启动时加载(loadOnStartup变量)、是否异步配置(asyncSupported)以及是否可被SCI覆盖配置(overridable)。
三、context启动过程
解析和装配web.xml到context的过程中,filter和listner只是将字符类型的变量设置到context中了,并没有真正实例化和启动。
StandardContext.startInternal()方法解析完web.xml后,接下来就是各组件的实例化和启动了。
1、 Set up the context init params。
2、 调用ServletContainerInitializer
3、Configure and call application event listeners(配置和调用Listener)
4、Configure and call application filters(配置和调用Filters)
5、Load and initialize all "load on startup" servlets,对设置了load on startup的Servlet进行初始化。
从以上步骤可以看出,主要组件的初始化顺序为:上下文参数->监听器->过滤器->Servlet。
protected synchronized void startInternal() throws LifecycleException {
//...
if (ok) {
//...
// Notify our interested LifecycleListeners(解析web.xml)
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
//...
}
//...
// We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());
// Initialize associated mapper
mapper.setContext(getPath(), welcomeFiles, resources);
//...
try {
// Create context attributes that will be required
if (ok) {
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}
// Set up the context init params
mergeParameters();
// … Call ServletContainerInitializers
// 配置和调用listeners
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
//...
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
//加载和初始化配置在load on startup的servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
//...
// Reinitializing if something went wrong
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}
四、监听器初始化和启动过程:
在上面分析的StandardContext的startInternal方法中,调用listenerStart方法实例化和启动Listener,具体代码如下:
public boolean listenerStart() {
//...
// Instantiate the required listeners
ApplicationListener listeners[] = applicationListeners;
Object results[] = new Object[listeners.length];
boolean ok = true;
for (int i = 0; i < results.length; i++) {
//...
try {
ApplicationListener listener = listeners[i];
results[i] = getInstanceManager().newInstance(
listener.getClassName());//创建实例
if (listener.isPluggabilityBlocked()) {
noPluggabilityListeners.add(results[i]);
}
} catch (Throwable t) {
//...
}
}
//...
// Sort listeners in two arrays
ArrayList eventListeners = new ArrayList();
ArrayList lifecycleListeners = new ArrayList();
for (int i = 0; i < results.length; i++) {
if ((results[i] instanceof ServletContextAttributeListener)
|| (results[i] instanceof ServletRequestAttributeListener)
|| (results[i] instanceof ServletRequestListener)
|| (results[i] instanceof HttpSessionAttributeListener)) {
eventListeners.add(results[i]);
}
if ((results[i] instanceof ServletContextListener)
|| (results[i] instanceof HttpSessionListener)) {
lifecycleListeners.add(results[i]);
}
}
// Listener instances may have been added directly to this Context by
// ServletContextInitializers and other code via the pluggability APIs.
// Put them these listeners after the ones defined in web.xml and/or
// annotations then overwrite the list of instances with the new, full
// list.
for (Object eventListener: getApplicationEventListeners()) {
eventListeners.add(eventListener);
}
setApplicationEventListeners(eventListeners.toArray());
for (Object lifecycleListener: getApplicationLifecycleListeners()) {
lifecycleListeners.add(lifecycleListener);
if (lifecycleListener instanceof ServletContextListener) {
noPluggabilityListeners.add(lifecycleListener);
}
}
setApplicationLifecycleListeners(lifecycleListeners.toArray());
//... Send application start events
// Ensure context is not null
getServletContext();
context.setNewServletContextListenerAllowed(false);
Object instances[] = getApplicationLifecycleListeners();
if (instances == null || instances.length == 0) {
return ok;
}
ServletContextEvent event = new ServletContextEvent(getServletContext());
ServletContextEvent tldEvent = null;
if (noPluggabilityListeners.size() > 0) {
noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
tldEvent = new ServletContextEvent(noPluggabilityServletContext);
}
for (int i = 0; i < instances.length; i++) {
if (instances[i] == null)
continue;
if (!(instances[i] instanceof ServletContextListener))
continue;
ServletContextListener listener =
(ServletContextListener) instances[i];
try {
fireContainerEvent("beforeContextInitialized", listener);
if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);//通知context容器正在初始化
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
//...
}
}
return (ok);
}
ServletContextListener监听容器ServletContext的启动,Spring就是通过自定义这种类型的监听器初始化的。它有两个接口:
contextInitialized:通知context容器正在初始化,在filter和servlet实例化之前执行。
contextDestroyed:通知servletcontext将要被关闭,在此之前,servlets和filters已经被销毁。
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者何欢授权发布。