深入刨析tomcat 之---第11篇 how tomcat works( 第15章 ) 如何解析web.xml 文件
writedby 张艳涛
记得当年是学习jsp的时候,写过web.xml中的标签.在之后的springmvc中也是有关于配置mvc 过滤器 和dispatchServlet的标签,之前是看不懂呢!看到这本how tomcat works之后,现在比较清楚了,
那么就写下自己的理解
在webapps/app1/目录下的web.xml文件 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>Modern</servlet-name> <servlet-class>ModernServlet</servlet-class> </servlet> <servlet> <servlet-name>Primitive</servlet-name> <servlet-class>PrimitiveServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Modern</servlet-name> <url-pattern>/Modern</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Primitive</servlet-name> <url-pattern>/Primitive</url-pattern> </servlet-mapping> </web-app>
这可是标准的web.xml文件,他的作用,用代码表示出来为
Wrapper wrapper1 = new StandardWrapper(); wrapper1.setName("Primitive"); wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new StandardWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("ModernServlet"); Context context = new StandardContext(); // StandardContext's start method adds a default mapper context.setPath("/app1"); context.setDocBase("app1"); context.addChild(wrapper1); context.addChild(wrapper2); LifecycleListener listener = new SimpleContextConfig(); ((Lifecycle) context).addLifecycleListener(listener); Host host = new StandardHost(); host.addChild(context); host.setName("localhost"); host.setAppBase("webapps"); Loader loader = new WebappLoader(); context.setLoader(loader); // context.addServletMapping(pattern, name); context.addServletMapping("/Primitive", "Primitive"); context.addServletMapping("/Modern", "Modern");
描述一下,上述行为,上述为tomcat实现的应编码,StandardContext 是一个可以代表一个web应用,这里是我们的app1应用, 对应的标签就是<web-app> ,
StandardWrapper是代表一个Servlet程序,这里就是是我们wrapper1,对应的标签就是<servlet>其中的行为
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
对应了
<servlet-name>Primitive</servlet-name>
<servlet-class>PrimitiveServlet</servlet-class>
接着就是配置映射关系
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
对应的标签为
<servlet-mapping>
<servlet-name>Modern</servlet-name>
<url-pattern>/Modern</url-pattern>
</servlet-mapping>
你说的看似是这么回事,那么到底tomcat是如何解析的呢?那么我们看源码说话
HostConfig,站点的配置文件看起
先解释一下这个host对象,这个是tomcat的4大容器之一,engine,host,context,wrapper; host代表的是一个站点,比如host的重要的属性有
appBase="webapps"
name="localhost"
children=context的HashMap
那么能看的到,deloyApps代码中是分析webapps目录下的文件,对于每个文件都要新建一个StandardContext对象
代码中能看到的设置
context.setPath(contextPath);
context.setDocBase(docBase);
和自己手写BootStrap文件行为是一致(bootStrap参见源码第15章),那么还是没有看到如何解析web.xml呢
解析web.xml在ContextConfig文件中,在start()方法中有
其中1是解析conf/web.xml文件的, 其2是解析web应用下的web.xml文件的
能看到使用一个Digester 去读取 jndi:/localhost/app1/WEB-INF/web.xml这个文件,新建一个Digester,重要的代码有
webDigester.push(context);
将context对象压入digester的内部栈,然后使用规则去解析web.xml文件,那么重要的是看解析规则,解析规则,在新建这个对象的时候添加的
看这个类中的方法
public void addRuleInstances(Digester digester) { digester.addRule(prefix + "web-app", new SetPublicIdRule(digester, "setPublicId")); digester.addCallMethod(prefix + "web-app/context-param", "addParameter", 2); digester.addCallParam(prefix + "web-app/context-param/param-name", 0); digester.addCallParam(prefix + "web-app/context-param/param-value", 1); digester.addCallMethod(prefix + "web-app/display-name", "setDisplayName", 0); digester.addRule(prefix + "web-app/distributable", new SetDistributableRule(digester)); digester.addObjectCreate(prefix + "web-app/ejb-local-ref", "org.apache.catalina.deploy.ContextLocalEjb"); digester.addSetNext(prefix + "web-app/ejb-local-ref", "addLocalEjb", "org.apache.catalina.deploy.ContextLocalEjb"); digester.addCallMethod(prefix + "web-app/ejb-local-ref/description", "setDescription", 0); digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-link", "setLink", 0); digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-name", "setName", 0); digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-type", "setType", 0); digester.addCallMethod(prefix + "web-app/ejb-local-ref/local", "setLocal", 0); digester.addCallMethod(prefix + "web-app/ejb-local-ref/local-home", "setHome", 0); digester.addObjectCreate(prefix + "web-app/ejb-ref", "org.apache.catalina.deploy.ContextEjb"); digester.addSetNext(prefix + "web-app/ejb-ref", "addEjb", "org.apache.catalina.deploy.ContextEjb"); digester.addCallMethod(prefix + "web-app/ejb-ref/description", "setDescription", 0); digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-link", "setLink", 0); digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-name", "setName", 0); digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-type", "setType", 0); digester.addCallMethod(prefix + "web-app/ejb-ref/home", "setHome", 0); digester.addCallMethod(prefix + "web-app/ejb-ref/remote", "setRemote", 0); digester.addObjectCreate(prefix + "web-app/env-entry", "org.apache.catalina.deploy.ContextEnvironment"); digester.addSetNext(prefix + "web-app/env-entry", "addEnvironment", "org.apache.catalina.deploy.ContextEnvironment"); digester.addCallMethod(prefix + "web-app/env-entry/description", "setDescription", 0); digester.addCallMethod(prefix + "web-app/env-entry/env-entry-name", "setName", 0); digester.addCallMethod(prefix + "web-app/env-entry/env-entry-type", "setType", 0); digester.addCallMethod(prefix + "web-app/env-entry/env-entry-value", "setValue", 0); digester.addObjectCreate(prefix + "web-app/error-page", "org.apache.catalina.deploy.ErrorPage"); digester.addSetNext(prefix + "web-app/error-page", "addErrorPage", "org.apache.catalina.deploy.ErrorPage"); digester.addCallMethod(prefix + "web-app/error-page/error-code", "setErrorCode", 0); digester.addCallMethod(prefix + "web-app/error-page/exception-type", "setExceptionType", 0); digester.addCallMethod(prefix + "web-app/error-page/location", "setLocation", 0); digester.addObjectCreate(prefix + "web-app/filter", "org.apache.catalina.deploy.FilterDef"); digester.addSetNext(prefix + "web-app/filter", "addFilterDef", "org.apache.catalina.deploy.FilterDef"); digester.addCallMethod(prefix + "web-app/filter/description", "setDescription", 0); digester.addCallMethod(prefix + "web-app/filter/display-name", "setDisplayName", 0); digester.addCallMethod(prefix + "web-app/filter/filter-class", "setFilterClass", 0); digester.addCallMethod(prefix + "web-app/filter/filter-name", "setFilterName", 0); digester.addCallMethod(prefix + "web-app/filter/large-icon", "setLargeIcon", 0); digester.addCallMethod(prefix + "web-app/filter/small-icon", "setSmallIcon", 0); digester.addCallMethod(prefix + "web-app/filter/init-param", "addInitParameter", 2); digester.addCallParam(prefix + "web-app/filter/init-param/param-name", 0); digester.addCallParam(prefix + "web-app/filter/init-param/param-value", 1); digester.addObjectCreate(prefix + "web-app/filter-mapping", "org.apache.catalina.deploy.FilterMap"); digester.addSetNext(prefix + "web-app/filter-mapping", "addFilterMap", "org.apache.catalina.deploy.FilterMap"); digester.addCallMethod(prefix + "web-app/filter-mapping/filter-name", "setFilterName", 0); digester.addCallMethod(prefix + "web-app/filter-mapping/servlet-name", "setServletName", 0); digester.addCallMethod(prefix + "web-app/filter-mapping/url-pattern", "setURLPattern", 0); digester.addCallMethod(prefix + "web-app/listener/listener-class", "addApplicationListener", 0); digester.addObjectCreate(prefix + "web-app/login-config", "org.apache.catalina.deploy.LoginConfig"); digester.addSetNext(prefix + "web-app/login-config", "setLoginConfig", "org.apache.catalina.deploy.LoginConfig"); digester.addCallMethod(prefix + "web-app/login-config/auth-method", "setAuthMethod", 0); digester.addCallMethod(prefix + "web-app/login-config/realm-name", "setRealmName", 0); digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-error-page", "setErrorPage", 0); digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-login-page", "setLoginPage", 0); digester.addCallMethod(prefix + "web-app/mime-mapping", "addMimeMapping", 2); digester.addCallParam(prefix + "web-app/mime-mapping/extension", 0); digester.addCallParam(prefix + "web-app/mime-mapping/mime-type", 1); digester.addCallMethod(prefix + "web-app/resource-env-ref", "addResourceEnvRef", 2); digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-name", 0); digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-type", 1); digester.addObjectCreate(prefix + "web-app/resource-ref", "org.apache.catalina.deploy.ContextResource"); digester.addSetNext(prefix + "web-app/resource-ref", "addResource", "org.apache.catalina.deploy.ContextResource"); digester.addCallMethod(prefix + "web-app/resource-ref/description", "setDescription", 0); digester.addCallMethod(prefix + "web-app/resource-ref/res-auth", "setAuth", 0); digester.addCallMethod(prefix + "web-app/resource-ref/res-ref-name", "setName", 0); digester.addCallMethod(prefix + "web-app/resource-ref/res-sharing-scope", "setScope", 0); digester.addCallMethod(prefix + "web-app/resource-ref/res-type", "setType", 0); digester.addObjectCreate(prefix + "web-app/security-constraint", "org.apache.catalina.deploy.SecurityConstraint"); digester.addSetNext(prefix + "web-app/security-constraint", "addConstraint", "org.apache.catalina.deploy.SecurityConstraint"); digester.addRule(prefix + "web-app/security-constraint/auth-constraint", new SetAuthConstraintRule(digester)); digester.addCallMethod(prefix + "web-app/security-constraint/auth-constraint/role-name", "addAuthRole", 0); digester.addCallMethod(prefix + "web-app/security-constraint/display-name", "setDisplayName", 0); digester.addCallMethod(prefix + "web-app/security-constraint/user-data-constraint/transport-guarantee", "setUserConstraint", 0); digester.addObjectCreate(prefix + "web-app/security-constraint/web-resource-collection", "org.apache.catalina.deploy.SecurityCollection"); digester.addSetNext(prefix + "web-app/security-constraint/web-resource-collection", "addCollection", "org.apache.catalina.deploy.SecurityCollection"); digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/http-method", "addMethod", 0); digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/url-pattern", "addPattern", 0); digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/web-resource-name", "setName", 0); digester.addCallMethod(prefix + "web-app/security-role/role-name", "addSecurityRole", 0); digester.addRule(prefix + "web-app/servlet", new WrapperCreateRule(digester)); digester.addSetNext(prefix + "web-app/servlet", "addChild", "org.apache.catalina.Container"); digester.addCallMethod(prefix + "web-app/servlet/init-param", "addInitParameter", 2); digester.addCallParam(prefix + "web-app/servlet/init-param/param-name", 0); digester.addCallParam(prefix + "web-app/servlet/init-param/param-value", 1); digester.addCallMethod(prefix + "web-app/servlet/jsp-file", "setJspFile", 0); digester.addCallMethod(prefix + "web-app/servlet/load-on-startup", "setLoadOnStartupString", 0); digester.addCallMethod(prefix + "web-app/servlet/run-as/role-name", "setRunAs", 0); digester.addCallMethod(prefix + "web-app/servlet/security-role-ref", "addSecurityReference", 2); digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-link", 1); digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-name", 0); digester.addCallMethod(prefix + "web-app/servlet/servlet-class", "setServletClass", 0); digester.addCallMethod(prefix + "web-app/servlet/servlet-name", "setName", 0); digester.addCallMethod(prefix + "web-app/servlet-mapping", "addServletMapping", 2); digester.addCallParam(prefix + "web-app/servlet-mapping/servlet-name", 1); digester.addCallParam(prefix + "web-app/servlet-mapping/url-pattern", 0); digester.addCallMethod(prefix + "web-app/session-config/session-timeout", "setSessionTimeout", 1, new Class[] { Integer.TYPE }); digester.addCallParam(prefix + "web-app/session-config/session-timeout", 0); digester.addCallMethod(prefix + "web-app/taglib", "addTaglib", 2); digester.addCallParam(prefix + "web-app/taglib/taglib-location", 1); digester.addCallParam(prefix + "web-app/taglib/taglib-uri", 0); digester.addCallMethod(prefix + "web-app/welcome-file-list/welcome-file", "addWelcomeFile", 0); }
对于规则分析如下
digester.addRule(prefix + "web-app",
new SetPublicIdRule(digester, "setPublicId"));
没做别的就是调用了context的 setPublicId方法
对于没设计的规则不解读,那么找 <servlet> 标签
那么遇到web-app/servlet的第一规则是WrapperCreateRule
begin中执行了,peek出栈中的context对象(就是之前push进去的代表app1的standardContext对象,
那么新建一个wrapper对象,将wrapper对象 push的digest栈中,
接下的规则是
digester.addSetNext(prefix + "web-app/servlet", "addChild", "org.apache.catalina.Container");
调用栈顶-1位置的对象的 addChild() 方法将栈顶对象作为参数,添加的context对象(栈顶-1位置对象)
接着遇到的标签是<servlet-name>Modern</servlet-name>
这就是调用wrapper对象的setname方法将参数 "Modern" 标签的值,放入的wrapper对象中
不就是调用了
Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
如下的语句吗?
确实是,到此就分析完毕了,
总结: 以上涉及了俩个问题1, tomcat是如何给host对象配置web.xml规则的StandardContext的,就是看webapps文件下有几个web应用,给每个新建一个StandardContext
在新建一个digester将context压栈,接着按规则解析web.xml.解析过程无非就是三种,一个新建对象,2调用对象方法,3 建立栈顶元素和栈顶-1位置元素的关系(addChild方法)
第二个问题是:digester解析web.xml的过程
结束: