死磕Tomcat7源码之二:web组件初始化
经过死磕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=newArrayList<Future<Void>>();for(inti=0;i<children.length;i++){results.add(startStopExecutor.submit(newStartChild(children[i])));}booleanfail=false;for(Future<Void>result:results){try{result.get();}catch(Exceptione){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
publicbooleanloadOnStartup(Containerchildren[]){//Collect"loadonstartup"servletsthatneedtobeinitializedTreeMap<Integer,ArrayList<Wrapper>>map=newTreeMap<Integer,ArrayList<Wrapper>>();for(inti=0;i<children.length;i++){Wrapperwrapper=(Wrapper)children[i];intloadOnStartup=wrapper.getLoadOnStartup();//如果小于0,跳过if(loadOnStartup<0)continue;Integerkey=Integer.valueOf(loadOnStartup);ArrayList<Wrapper>list=map.get(key);if(list==null){list=newArrayList<Wrapper>();map.put(key,list);}list.add(wrapper);}//Loadthecollected"loadonstartup"servletsfor(ArrayList<Wrapper>list:map.values()){for(Wrapperwrapper:list){try{//完成加载wrapper.load();}catch(ServletExceptione){getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",getName(),wrapper.getName()),StandardWrapper.getRootCause(e));//NOTE:loaderrors(includingaservletthatthrows//UnavailableExceptionfromtheinit()method)areNOT//fataltoapplicationstartup//unlessfailCtxIfServletStartFails="true"isspecifiedif(getComputedFailCtxIfServletStartFails()){returnfalse;}}}}returntrue;}
通过源码分析,我们清楚了load-on-startup 小于0时,表示servlet不需要在启动时初始化。
protectedvolatilebooleaninstanceInitialized=false;publicsynchronizedvoidload()throwsServletException{instance=loadServlet();if(!instanceInitialized){initServlet(instance);}if(isJspServlet){//}}
通过分析load()方法, 主要逻辑保证Servlet初始化一次。注意instanceInitialized 变量声明为volatile类型,保证线程安全。
privatesynchronizedvoidinitServlet(Servletservlet)throwsServletException{//Calltheinitializationmethodofthisservlettry{instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);if(Globals.IS_SECURITY_ENABLED){booleansuccess=false;try{Object[]args=newObject[]{facade};SecurityUtil.doAsPrivilege("init",servlet,classType,args);success=true;}finally{if(!success){//destroy()willnotbecalled,thusclearthereferencenowSecurityUtil.remove(servlet);}}}else{servlet.init(facade);}instanceInitialized=true;instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet);}catch(UnavailableExceptionf){...}}
initServlet方法,主要调用servlet的init方法,完成servlet组件的初始化工作,以及触发beforeInit和afterInit事件,触发操作org.apache.catalina.InstanceListener.instanceEvent(InstanceEvent event)。
4.listener的初始化过程
4.1 listener类结构图
4.2listenerStart方法
/***Configurethesetofinstantiatedapplicationeventlisteners*forthisContext.Return<code>true</code>ifalllistenerswre*initializedsuccessfully,or<code>false</code>otherwise.*/publicbooleanlistenerStart(){//SortlistenersintwoarraysArrayList<Object>eventListeners=newArrayList<Object>();ArrayList<Object>lifecycleListeners=newArrayList<Object>();for(inti=0;i<results.length;i++){if((results[i]instanceofServletContextAttributeListener)||(results[i]instanceofServletRequestAttributeListener)||(results[i]instanceofServletRequestListener)||(results[i]instanceofHttpSessionAttributeListener)){eventListeners.add(results[i]);}if((results[i]instanceofServletContextListener)||(results[i]instanceofHttpSessionListener)){lifecycleListeners.add(results[i]);}}...for(inti=0;i<instances.length;i++){if(instances[i]==null)continue;if(!(instances[i]instanceofServletContextListener))continue;ServletContextListenerlistener=(ServletContextListener)instances[i];try{fireContainerEvent("beforeContextInitialized",listener);if(noPluggabilityListeners.contains(listener)){listener.contextInitialized(tldEvent);}else{listener.contextInitialized(event);}fireContainerEvent("afterContextInitialized",listener);}catch(Throwablet){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名称eventListenerServletContextAttributeListenereventListenerServletRequestAttributeListenereventListenerServletRequestListenereventListenerHttpSessionAttributeListenerlifecycleListenerHttpSessionListenerlifecycleListener
noPluggabilityListener
ServletContextListener5.filter 初始化过程
5.1filterStart方法,遍历FilterDefs,初始化Filter,并放入filterConfigsMap。
这儿维护的filterConfigsMap,将来会在StandardWrapperValve.invoke方法中调用。而StandardWrapperValve作为servlet为pipeline模式中处理用户请求流程中的一个节点,所以也就实现了filter拦截请求的目的。
publicbooleanfilterStart(){if(getLogger().isDebugEnabled())getLogger().debug("Startingfilters");//InstantiateandrecordaFilterConfigforeachdefinedfilterbooleanok=true;synchronized(filterConfigs){filterConfigs.clear();for(Entry<String,FilterDef>entry:filterDefs.entrySet()){Stringname=entry.getKey();if(getLogger().isDebugEnabled())getLogger().debug("Startingfilter'"+name+"'");ApplicationFilterConfigfilterConfig=null;try{filterConfig=newApplicationFilterConfig(this,entry.getValue());filterConfigs.put(name,filterConfig);}catch(Throwablet){t=ExceptionUtils.unwrapInvocationTargetException(t);ExceptionUtils.handleThrowable(t);getLogger().error(sm.getString("standardContext.filterStart",name),t);ok=false;}}}return(ok);}
5.2ApplicationFilterConfig构造函数
ApplicationFilterConfig(Contextcontext,FilterDeffilterDef)throwsClassCastException,ClassNotFoundException,IllegalAccessException,InstantiationException,ServletException,InvocationTargetException,NamingException{super();this.context=context;this.filterDef=filterDef;//Allocateanewfilterinstanceifnecessaryif(filterDef.getFilter()==null){getFilter();}else{this.filter=filterDef.getFilter();getInstanceManager().newInstance(filter);initFilter();}}FiltergetFilter()throwsClassCastException,ClassNotFoundException,IllegalAccessException,InstantiationException,ServletException,InvocationTargetException,NamingException{//Returntheexistingfilterinstance,ifanyif(this.filter!=null)return(this.filter);//IdentifytheclassloaderwewillbeusingStringfilterClass=filterDef.getFilterClass();//构造filter对象this.filter=(Filter)getInstanceManager().newInstance(filterClass);initFilter();return(this.filter);}privatevoidinitFilter()throwsServletException{if(contextinstanceofStandardContext&&context.getSwallowOutput()){try{SystemLogHandler.startCapture();filter.init(this);//调用filter的初始化}finally{Stringcapturedlog=SystemLogHandler.stopCapture();if(capturedlog!=null&&capturedlog.length()>0){getServletContext().log(capturedlog);}}}else{filter.init(this);}//ExposefilterviaJMXregisterJMX();}
最后,组件已经初始化了。现在理所当然应该可以处理用户请求了。都知道我们用户请求信息都在HttpServletRequest对象中。那么用户的 HTTP socket流信息,怎么一步步转换成HttpServletRequest对象的呢。下次详聊。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。