您的位置:首页 > 运维架构 > Tomcat

死磕Tomcat7源码之二:web组件初始化

2016-05-22 11:44 597 查看
经过死磕Tomcat7源码之一:解析web.xml,已经知道webapp的配置信息是如何解析到内存中。接下来,就是如何将对应的组件对象初始化化。分析所有的组件初始化过程,根本不可能。本文重点针对阐明3个主要组件的初始化过程,分别是:servlet,listener,filter。通过本文,你可以掌握以下知识点
了解组件初始化调用序列

组件servlet,listener,filter组件的初始化顺序

listener的初始化过程

servlet的初始化过程

filter的初始化过程

1.组件初始化序列
通过《解析web.xml》,我们可以了解,tomcat在自动完成webapp应用的部署时,完成了web.xml信息的解析,也就是说webapp组件配置元信息,tomcat已经拿到了。接下来,就是根据配置的元信息规则,初始化组件。



通过上图,知道组件对象的初始化主要从StandardContext完成的, StandardContext对应着耳熟能详的应用,比如webapp目录下的 docs,ROOT,manager...。
1组件初始化过程使用的是线程启动的。
代码参考org.apache.catalina.core.ContainerBase.startInternal
List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}

boolean fail = false;
for (Future<Void> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}

}
2.组件servlet,listener,filter组件的初始化顺序
笔者,创建了1个名为"helloapp"的webapp应用,分别声明了多个lister,多个servlet,filter。通过多次调整各组件在web.xml中的相对顺序。得出如下结论。
组件执行顺序按Listener,Filter,Servlet进行。

Servlet的load-on-startup影响Servlet的启动顺序,详情见2.1节说明

Filter之间的初始化顺序,与<filter-name>中的字符排序规则有关,经测试与默认排序规则相反。

Listener之间的初始化顺序,与在web.xml声明的顺序一致。这一点非常重要,如果使用一些mvc框架,安全框架时,如果使用Listener来完成过滤拦截的话,一定要注意Listener的声明顺序。

2.1servlet中load-on-startup的规则说明

标记容器是否在启动的时候就加载这个servlet。

当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;

当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。

正数的值越小,启动该servlet的优先级越高。

3.servlet的初始化过程

org.apache.catalina.core.StandardWrapper
public boolean loadOnStartup(Container children[]) {

// Collect "load on startup" servlets that need to be initialized
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();
//如果小于0,跳过
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("standardContext.loadOnStartup.loadException",
getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
// unless failCtxIfServletStartFails="true" is specified
if(getComputedFailCtxIfServletStartFails()) {
return false;
}
}
}
}
return true;

}
通过源码分析,我们清楚了load-on-startup 小于0时,表示servlet不需要在启动时初始化。
protected volatile boolean instanceInitialized = false;
public synchronized void load() throws ServletException {
instance = loadServlet();

if (!instanceInitialized) {
initServlet(instance);
}

if (isJspServlet) {
//
}
}
通过分析load()方法, 主要逻辑保证Servlet初始化一次。注意instanceInitialized 变量声明为volatile类型,保证线程安全。
private synchronized void initServlet(Servlet servlet)
throws ServletException {
// Call the initialization method of this servlet
try {
instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
servlet);

if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
servlet.init(facade);
}

instanceInitialized = true;

instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
servlet);

} catch (UnavailableException f) {
...
}
}
initServlet方法,主要调用servlet的init方法,完成servlet组件的初始化工作,以及触发beforeInit和afterInit事件,触发操作org.apache.catalina.InstanceListener.instanceEvent(InstanceEvent event)。

4.listener的初始化过程
4.1 listener类结构图


4.2 listenerStart方法
/**
* Configure the set of instantiated application event listeners
* for this Context.  Return <code>true</code> if all listeners wre
* initialized successfully, or <code>false</code> otherwise.
*/
public boolean listenerStart() {

// Sort listeners in two arrays
ArrayList<Object> eventListeners = new ArrayList<Object>();
ArrayList<Object> lifecycleListeners = new ArrayList<Object>();
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]);
}
}
...
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);
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
fireContainerEvent("afterContextInitialized", listener);
getLogger().error
(sm.getString("standardContext.listenerStart",
instances[i].getClass().getName()), t);
ok = false;
}
}
return (ok);

}
通过分析listenerStart方法片段,可以知道tomcat将listener分为2类,分别是eventListener和lifecycleListener两大类。并将listener加入到StandardHost中,并触发beforeContextInitialized事件,和afterContextInitialized事件。
类型Listener名称
eventListenerServletContextAttributeListener
eventListenerServletRequestAttributeListener
eventListenerServletRequestListener
eventListenerHttpSessionAttributeListener
lifecycleListenerHttpSessionListener
lifecycleListener
noPluggabilityListener
ServletContextListener
5.filter 初始化过程


5.1 filterStart方法,遍历FilterDefs,初始化Filter,并放入filterConfigsMap。
这儿维护的filterConfigsMap,将来会在StandardWrapperValve.invoke方法中调用。而StandardWrapperValve作为servlet为pipeline模式中处理用户请求流程中的一个节点,所以也就实现了filter拦截请求的目的。
public boolean filterStart() {

if (getLogger().isDebugEnabled())
getLogger().debug("Starting filters");
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
for (Entry<String, FilterDef> entry : filterDefs.entrySet()) {
String name = entry.getKey();
if (getLogger().isDebugEnabled())
getLogger().debug(" Starting filter '" + name + "'");
ApplicationFilterConfig filterConfig = null;
try {
filterConfig =
new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
getLogger().error
(sm.getString("standardContext.filterStart", name), t);
ok = false;
}
}
}

return (ok);

}
5.2 ApplicationFilterConfig构造函数
ApplicationFilterConfig(Context context, FilterDef filterDef)
throws ClassCastException, ClassNotFoundException,
IllegalAccessException, InstantiationException,
ServletException, InvocationTargetException, NamingException {

super();

this.context = context;
this.filterDef = filterDef;
// Allocate a new filter instance if necessary
if (filterDef.getFilter() == null) {
getFilter();
} else {
this.filter = filterDef.getFilter();
getInstanceManager().newInstance(filter);
initFilter();
}
}

Filter getFilter() throws ClassCastException, ClassNotFoundException,
IllegalAccessException, InstantiationException, ServletException,
InvocationTargetException, NamingException {

// Return the existing filter instance, if any
if (this.filter != null)
return (this.filter);

// Identify the class loader we will be using
String filterClass = filterDef.getFilterClass();
//构造filter对象
this.filter = (Filter) getInstanceManager().newInstance(filterClass);

initFilter();

return (this.filter);

}

private void initFilter() throws ServletException {
if (context instanceof StandardContext &&
context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
filter.init(this);//调用filter的初始化
} finally {
String capturedlog = SystemLogHandler.stopCapture();
if (capturedlog != null && capturedlog.length() > 0) {
getServletContext().log(capturedlog);
}
}
} else {
filter.init(this);
}

// Expose filter via JMX
registerJMX();
}


最后,组件已经初始化了。现在理所当然应该可以处理用户请求了。都知道我们用户请求信息都在HttpServletRequest对象中。那么用户的 HTTP socket流信息,怎么一步步转换成HttpServletRequest对象的呢。下次详聊。

本文出自 “简单” 博客,请务必保留此出处http://dba10g.blog.51cto.com/764602/1775827
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: