死磕Tomcat7源码之一:解析web.xml
熟悉java web开发的同学都清楚,tomcat作为一款非常流行的servlet容器,开源,流行,配置简单,不需要赘述。个人认为,web.xml作为webapp的入口,弄清楚该文件的底层解析过程,进而可以窥探tomcat的底层工作机制,搞明白tomcat对servlert规范的实现机理。
通过本文,可以知道以下部分内容
webapp部署3种部署方式
webapp web.xml解析流程
webapp Context对象信息的生成(不包括对象的生成)
总体来说,webapp部署有三种方式:XML文件描述符、WAR包、文件目录。三种方式部署的总体流程很相似,都是一个web应用分配一个线程来处理,这里统一放到与Host内部的线程池对象中(startStopExecutor),所以有时会看到在默认配置下Tomcat启动后可能有一个叫“-startStop-”的线程还会运行一段时间才结束。但浏览这三种部署方式的实现代码,里面都是构建一个Context对象,并将构建好的Context对象与Host组件关联起来。
1.三种部署方式
/***部署应用,该方法被org.apache.catalina.startup.HostConfig.start()调用*包含3种部署方式,每个部署方式分别使用内部类,分配一个线程处理*/protectedvoiddeployApps(){FileappBase=appBase();FileconfigBase=configBase();String[]filteredAppPaths=filterAppPaths(appBase.list());//DeployXMLdescriptorsfromconfigBasedeployDescriptors(configBase,configBase.list());//DeployWARsdeployWARs(appBase,filteredAppPaths);//DeployexpandedfoldersdeployDirectories(appBase,filteredAppPaths);}
3个部署的内部类
org.apache.catalina.startup.HostConfig.DeployWar
org.apache.catalina.startup.HostConfig.DeployDescriptor
org.apache.catalina.startup.HostConfig.DeployDirectory
2.从部署webapp到解析web.xml序列图
找了很多网上资料,对从部署webapp开始,到开始解析web.xml这一段处理过程,没找到相关资料。个人就花时间整理出这个序列图,填补下这方面的空缺。通过该图,可以很清楚的知道,这部分主要完成了2件事:
(1)启动StandardContext,并将context对象添加到StandardHost对象中。
(2)通过触发事件机制,开始Context的解析过程。
3.web.xml解析过程
接第二步骤中序列图,开始分析web.xml的解析过程。从ContextConfig开始。
3.1 org.apache.catalina.startup.ContextConfig.configureStart()
/***Processa"contextConfig"eventforthisContext.*/protectedsynchronizedvoidconfigureStart(){//CalledfromStandardContext.start()webConfig();if(ok){validateSecurityRoles();}//Configureanauthenticatorifweneedoneif(ok)authenticatorConfig();//Makeourapplicationavailableifnoproblemswereencounteredif(ok)context.setConfigured(true);else{log.error(sm.getString("contextConfig.unavailable"));context.setConfigured(false);}}
该方法通过调用webConfig(),具体完成解析工作,此外完成了安全验证相关内容。
3.2 org.apache.catalina.startup.ContextConfig.webConfig()方法
/***Scantheweb.xmlfilesthatapplytothewebapplicationandmergethem*usingtherulesdefinedinthespec.Fortheglobalweb.xmlfiles,*wherethereisduplicateconfiguration,themostspecificlevelwins.ie*anapplication'sweb.xmltakesprecedenceoverthehostlevelorglobal*web.xmlfile.*/protectedvoidwebConfig(){Set<WebXml>defaults=newHashSet<WebXml>();defaults.add(getDefaultWebXmlFragment());WebXmlwebXml=createWebXml();//Parsecontextlevelweb.xmlInputSourcecontextWebXml=getContextWebXmlSource();parseWebXml(contextWebXml,webXml,false);//解析ServletContextsContext=context.getServletContext();//Orderingisimportanthere//Step1.IdentifyalltheJARspackagedwiththeapplication//IftheJARshaveaweb-fragment.xmlitwillbeparsedatthis//point.Map<String,WebXml>fragments=processJarsForWebFragments(webXml);//Step2.Orderthefragments.Set<WebXml>orderedFragments=null;orderedFragments=WebXml.orderWebFragments(webXml,fragments,sContext);//Step3.LookforServletContainerInitializerimplementationsif(ok){processServletContainerInitializers();}//Step5.ProcessJARsforannotations-onlyneedtoprocess//thosefragmentswearegoingtouseif(ok){processAnnotations(orderedFragments,webXml.isMetadataComplete());}//Cache,ifused,isnolongerrequiredsoclearitjavaClassCache.clear();}//Step6.Mergeweb-fragment.xmlfilesintothemainweb.xml//Step7.ApplyglobaldefaultswebXml.merge(defaults);//Step8.ConvertexplicitlymentionedjspstoservletsconvertJsps(webXml);//Step9.Applymergedweb.xmltoContextwebXml.configureContext(context);//Step9a.Makethemergedweb.xmlavailabletoother//components,//Alwaysneedtolookforstaticresources//Step10.LookforstaticresourcespackagedinJARsproce***esourceJARs(resourceJars);//SeealsoStandardContext.resourcesStart()for//WEB-INF/classes/META-INF/resourcesconfiguration//Step11.ApplytheServletContainerInitializerconfigtothe//contextcontext.addServletContainerInitializer(entry.getKey(),entry.getValue());}
通过源码中的注释,step1 到step11。主要工作包含:1.解析xml,2.合并xml,3.组装Context,4.编译JSP。具体步骤,参考说明
扫描应用打包的所有Jar来检索Jar包里面的web.xml配置并解析,放入内存。
对这些已经检索到的web配置进行排序。
基于SPI机制查找ServletContainerInitializer的实现,写web中间件的同学注意了,了解SPI以及 ServletContainerInitializer机制这对于你来说可能是一个很好的知识点。
处理/WEB-INF/classes下面的类的注解,某个版本Servlet支持注解方式的配置,可以猜测相关事宜就是在这里干的。
处理Jar包中的注解类。
将web配置按照一定规则合并到一起。
应用全局默认配置,还记得Tomcat包下面的conf文件夹下面有个web.xml配置文件吧。
将JSP转换为Servlet,这让我想起了若干年前对JSP的理解。
将web配置应用到Servlet上下文,也即Servlet容器。
将配置信息保存起来以供其他组件访问,使得其他组件不需要再次重复上面的步骤去获取配置信息了。
检索Jar包中的静态资源。
将ServletContainerInitializer配置到上下文。
3.3 org.apache.catalina.startup.ContextConfig.parseWebXml方法
/***Parsesthegivensourceandstorestheparseddatainthegivenweb.xml*representation.Thebytestreamwillbeclosedattheendoftheparse*operation.**@paramsourceInputsourcecontainingtheXMLdatatobeparsed*@paramdestTheobjectrepresentationofcommonelementsofweb.xmland*web-fragment.xml*@paramfragmentSpecifieswhetherthesourceisweb-fragment.xmlor*web.xml*/protectedvoidparseWebXml(InputSourcesource,WebXmldest,booleanfragment){XmlErrorHandlerhandler=newXmlErrorHandler();Digesterdigester;WebRuleSetruleSet;if(fragment){digester=webFragmentDigester;ruleSet=webFragmentRuleSet;}else{digester=webDigester;ruleSet=webRuleSet;}digester.push(dest);digester.setErrorHandler(handler);digester.parse(source);}
使用Digester 对象即系web.xml,并将结果保存到WebXml对象中。
3.4 Digester的解析规则
(1)构造Digester.createWebXmlDigester
publicvoidcreateWebXmlDigester(booleannamespaceAware,booleanvalidation){booleanblockExternal=context.getXmlBlockExternal();webRuleSet=newWebRuleSet(false);webDigester=DigesterFactory.newDigester(validation,namespaceAware,webRuleSet,blockExternal);webDigester.getParser();webFragmentRuleSet=newWebRuleSet(true);webFragmentDigester=DigesterFactory.newDigester(validation,namespaceAware,webFragmentRuleSet,blockExternal);webFragmentDigester.getParser();}
(2)配置解析规则 WebRuleSet.addRuleInstances
/***<p>AddthesetofRuleinstancesdefinedinthisRuleSettothe*specified<code>Digester</code>instance,associatingthemwith*ournamespaceURI(ifany).Thismethodshouldonlybecalled*byaDigesterinstance.</p>**@paramdigesterDigesterinstancetowhichthenewRuleinstances*shouldbeadded.*/@OverridepublicvoidaddRuleInstances(Digesterdigester){digester.addRule(fullPrefix,newSetPublicIdRule("setPublicId"));digester.addRule(fullPrefix,newIgnoreAnnotationsRule());digester.addRule(fullPrefix,newVersionRule());//Requiredforbothfragmentsandnon-fragmentsdigester.addRule(fullPrefix+"/absolute-ordering",absoluteOrdering);digester.addRule(fullPrefix+"/ordering",relativeOrdering);if(fragment){//web-fragment.xmldigester.addRule(fullPrefix+"/name",name);digester.addCallMethod(fullPrefix+"/ordering/after/name","addAfterOrdering",0);digester.addCallMethod(fullPrefix+"/ordering/after/others","addAfterOrderingOthers");digester.addCallMethod(fullPrefix+"/ordering/before/name","addBeforeOrdering",0);digester.addCallMethod(fullPrefix+"/ordering/before/others","addBeforeOrderingOthers");}else{//web.xmldigester.addCallMethod(fullPrefix+"/absolute-ordering/name","addAbsoluteOrdering",0);digester.addCallMethod(fullPrefix+"/absolute-ordering/others","addAbsoluteOrderingOthers");}digester.addCallMethod(fullPrefix+"/context-param","addContextParam",2);digester.addCallParam(fullPrefix+"/context-param/param-name",0);digester.addCallParam(fullPrefix+"/context-param/param-value",1);digester.addObjectCreate(fullPrefix+"/filter","org.apache.catalina.deploy.FilterDef");digester.addSetNext(fullPrefix+"/filter","addFilter","org.apache.catalina.deploy.FilterDef");digester.addCallMethod(fullPrefix+"/filter/description","setDescription",0);digester.addCallMethod(fullPrefix+"/filter/display-name","setDisplayName",0);digester.addCallMethod(fullPrefix+"/filter/filter-class","setFilterClass",0);digester.addCallMethod(fullPrefix+"/filter/filter-name","setFilterName",0);digester.addCallMethod(fullPrefix+"/filter/icon/large-icon","setLargeIcon",0);digester.addCallMethod(fullPrefix+"/filter/icon/small-icon","setSmallIcon",0);digester.addCallMethod(fullPrefix+"/filter/async-supported","setAsyncSupported",0);digester.addCallMethod(fullPrefix+"/filter/init-param","addInitParameter",2);digester.addCallParam(fullPrefix+"/filter/init-param/param-name",0);digester.addCallParam(fullPrefix+"/filter/init-param/param-value",1);digester.addObjectCreate(fullPrefix+"/filter-mapping","org.apache.catalina.deploy.FilterMap");digester.addSetNext(fullPrefix+"/filter-mapping","addFilterMapping","org.apache.catalina.deploy.FilterMap");digester.addCallMethod(fullPrefix+"/filter-mapping/filter-name","setFilterName",0);digester.addCallMethod(fullPrefix+"/filter-mapping/servlet-name","addServletName",0);digester.addCallMethod(fullPrefix+"/filter-mapping/url-pattern","addURLPattern",0);digester.addCallMethod(fullPrefix+"/filter-mapping/dispatcher","setDispatcher",0);digester.addCallMethod(fullPrefix+"/listener/listener-class","addListener",0);...}
在这个方法里,可以看到熟悉的“/servlet/servlet-name”,"/listener/listener-class"等等。稍微懂点Digester解析语法的基础的朋友,立刻可以知道这儿就是解析规则所在,Digester解析web.xml规则都是在此配置的。进一步梳理下,可以弄明白servlet,filter等重要对象的数据载体。
标签数据载体类
/filter
org.apache.catalina.deploy.FilterDef/error-page
org.apache.catalina.deploy.ErrorPage/servlet
org.apache.catalina.deploy.ServletDef
...
部分私有内部Rule列表
通过分析,可以知道web.xml通过解析之后,配置信息都保存在WebXml对象中了。
WebXml中持有FilterMap,ServletDef,FilterDef等等对象的聚集信息。接下来tomcat就可以按照servlet规范初始化里面的组件了,有空将进一步介绍。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。