博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
死磕Tomcat7源码之一:解析web.xml
阅读量:7065 次
发布时间:2019-06-28

本文共 10891 字,大约阅读时间需要 36 分钟。

原创作品,允许转载,转载时请务必以超链接形式标明文章   、作者信息和本声明。否则将追究法律责任。

熟悉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.三种部署方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    
/**
     
* 部署应用,该方法被org.apache.catalina.startup.HostConfig.start()调用
     
* 包含3种部署方式,每个部署方式分别使用内部类,分配一个线程处理
     
*/
    
protected 
void 
deployApps() {
 
        
File appBase = appBase();
        
File configBase = configBase();
        
String[] filteredAppPaths = filterAppPaths(appBase.list());
        
// Deploy XML descriptors from configBase
        
deployDescriptors(configBase, configBase.list());
        
// Deploy WARs
        
deployWARs(appBase, filteredAppPaths);
        
// Deploy expanded folders
        
deployDirectories(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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   
/**
     
* Process a "contextConfig" event for this Context.
     
*/
    
protected 
synchronized 
void 
configureStart() {
        
// Called from StandardContext.start()
 
        
webConfig();
 
        
if 
(ok) {
            
validateSecurityRoles();
        
}
        
// Configure an authenticator if we need one
        
if 
(ok)
            
authenticatorConfig();
        
// Make our application available if no problems were encountered
        
if 
(ok)
            
context.setConfigured(
true
);
        
else 
{
            
log.error(sm.getString(
"contextConfig.unavailable"
));
            
context.setConfigured(
false
);
        
}
    
}

该方法通过调用webConfig(),具体完成解析工作,此外完成了安全验证相关内容。

3.2 org.apache.catalina.startup.ContextConfig.webConfig()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
   
/**
     
* Scan the web.xml files that apply to the web application and merge them
     
* using the rules defined in the spec. For the global web.xml files,
     
* where there is duplicate configuration, the most specific level wins. ie
     
* an application's web.xml takes precedence over the host level or global
     
* web.xml file.
     
*/
    
protected 
void 
webConfig() {
        
        
Set<WebXml> defaults = 
new 
HashSet<WebXml>();
        
defaults.add(getDefaultWebXmlFragment());
        
WebXml webXml = createWebXml();
 
        
// Parse context level web.xml
        
InputSource contextWebXml = getContextWebXmlSource();
        
parseWebXml(contextWebXml, webXml, 
false
);
//解析
 
        
ServletContext sContext = context.getServletContext();
 
        
// Ordering is important here
        
// Step 1. Identify all the JARs packaged with the application
        
// If the JARs have a web-fragment.xml it will be parsed at this
        
// point.
        
Map<String,WebXml> fragments = processJarsForWebFragments(webXml);
 
        
// Step 2. Order the fragments.
        
Set<WebXml> orderedFragments = 
null
;
        
orderedFragments =
                
WebXml.orderWebFragments(webXml, fragments, sContext);
 
        
// Step 3. Look for ServletContainerInitializer implementations
        
if 
(ok) {
            
processServletContainerInitializers();
        
}
 
 
            
// Step 5. Process JARs for annotations - only need to process
            
// those fragments we are going to use
            
if 
(ok) {
                
processAnnotations(
                        
orderedFragments, webXml.isMetadataComplete());
            
}
 
            
// Cache, if used, is no longer required so clear it
            
javaClassCache.clear();
        
}
                    
// Step 6. Merge web-fragment.xml files into the main web.xml
                    
// Step 7. Apply global defaults
              
webXml.merge(defaults);
               
// Step 8. Convert explicitly mentioned jsps to servlets
                    
convertJsps(webXml);
                     
// Step 9. Apply merged web.xml to Context
                    
webXml.configureContext(context);
 
        
// Step 9a. Make the merged web.xml available to other
        
// components, 
 
        
// Always need to look for static resources
        
// Step 10. Look for static resources packaged in JARs
                
processResourceJARs(resourceJars);
                
// See also StandardContext.resourcesStart() for
                
// WEB-INF/classes/META-INF/resources configuration
 
        
// Step 11. Apply the ServletContainerInitializer config to the
        
// context
                    
context.addServletContainerInitializer(
                            
entry.getKey(), entry.getValue());
    
}

通过源码中的注释,step1 到step11。主要工作包含:1.解析xml,2.合并xml,3.组装Context,4.编译JSP。具体步骤,参考

  1. 扫描应用打包的所有Jar来检索Jar包里面的web.xml配置并解析,放入内存。

  2. 对这些已经检索到的web配置进行排序。

  3. 基于SPI机制查找ServletContainerInitializer的实现,写web中间件的同学注意了,了解SPI以及                           ServletContainerInitializer机制这对于你来说可能是一个很好的知识点。

  4. 处理/WEB-INF/classes下面的类的注解,某个版本Servlet支持注解方式的配置,可以猜测相关事宜就是在这里干的。

  5. 处理Jar包中的注解类。

  6. 将web配置按照一定规则合并到一起。

  7. 应用全局默认配置,还记得Tomcat包下面的conf文件夹下面有个web.xml配置文件吧。

  8. 将JSP转换为Servlet,这让我想起了若干年前对JSP的理解。

  9. 将web配置应用到Servlet上下文,也即Servlet容器。

  10. 将配置信息保存起来以供其他组件访问,使得其他组件不需要再次重复上面的步骤去获取配置信息了。

  11. 检索Jar包中的静态资源。

  12. 将ServletContainerInitializer配置到上下文。

3.3 org.apache.catalina.startup.ContextConfig.parseWebXml方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
/**
     
* Parses the given source and stores the parsed data in the given web.xml
     
* representation. The byte stream will be closed at the end of the parse
     
* operation.
     
*
     
* @param source Input source containing the XML data to be parsed
     
* @param dest The object representation of common elements of web.xml and
     
*             web-fragment.xml
     
* @param fragment Specifies whether the source is web-fragment.xml or
     
*                 web.xml
     
*/
    
protected 
void 
parseWebXml(InputSource source, WebXml dest,
            
boolean 
fragment) {
        
XmlErrorHandler handler = 
new 
XmlErrorHandler();
 
        
Digester digester;
        
WebRuleSet ruleSet;
        
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    
public 
void 
createWebXmlDigester(
boolean 
namespaceAware,
            
boolean 
validation) {
 
        
boolean 
blockExternal = context.getXmlBlockExternal();
         
        
webRuleSet = 
new 
WebRuleSet(
false
);
        
webDigester = DigesterFactory.newDigester(validation,
                
namespaceAware, webRuleSet, blockExternal);
        
webDigester.getParser();
 
        
webFragmentRuleSet = 
new 
WebRuleSet(
true
);
        
webFragmentDigester = DigesterFactory.newDigester(validation,
                
namespaceAware, webFragmentRuleSet, blockExternal);
        
webFragmentDigester.getParser();
    
}

(2)配置解析规则 WebRuleSet.addRuleInstances

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  
/**
     
* <p>Add the set of Rule instances defined in this RuleSet to the
     
* specified <code>Digester</code> instance, associating them with
     
* our namespace URI (if any).  This method should only be called
     
* by a Digester instance.</p>
     
*
     
* @param digester Digester instance to which the new Rule instances
     
*  should be added.
     
*/
    
@Override
    
public 
void 
addRuleInstances(Digester digester) {
        
digester.addRule(fullPrefix,
                         
new 
SetPublicIdRule(
"setPublicId"
));
        
digester.addRule(fullPrefix,
                         
new 
IgnoreAnnotationsRule());
        
digester.addRule(fullPrefix,
                
new 
VersionRule());
 
        
// Required for both fragments and non-fragments
        
digester.addRule(fullPrefix + 
"/absolute-ordering"
, absoluteOrdering);
        
digester.addRule(fullPrefix + 
"/ordering"
, relativeOrdering);
 
        
if 
(fragment) {
            
// web-fragment.xml
            
digester.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.xml
            
digester.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

/filter-mapping org.apache.catalina.deploy.FilterMap
/login-config org.apache.catalina.deploy.LoginConfig
/session-config org.apache.catalina.deploy.SessionConfig
...
...

部分私有内部Rule列表

通过分析,可以知道web.xml通过解析之后,配置信息都保存在WebXml对象中了。

WebXml中持有FilterMap,ServletDef,FilterDef等等对象的聚集信息。接下来tomcat就可以按照servlet规范初始化里面的组件了,有空将进一步介绍。

本文出自 “” 博客,请务必保留此出处

你可能感兴趣的文章
java中三种主流数据库数据库(sqlserver,db2,oracle)的jdbc连接总结
查看>>
Oracle Apps AutoConfig
查看>>
[leetcode]Flatten Binary Tree to Linked List
查看>>
css颜色代码大全:(网页设计师和平面设计师常用)
查看>>
boost 1.52在windows下的配置
查看>>
素材锦囊——50个高质量的 PSD 素材免费下载《上篇》
查看>>
【转】oc中消息传递机制-附:对performSelector方法的扩充
查看>>
oracle的nvl和sql server的isnull
查看>>
[转]虚拟机下Ubuntu共享主机文件(Ubuntu、VMware、共享)
查看>>
高血压 治疗 偏方
查看>>
HtmlAttribute HTML属性处理类
查看>>
[书目20130316]jQuery UI开发指南
查看>>
Sql Server系列:开发存储过程
查看>>
Find INTCOL#=1001 in col_usage$?
查看>>
AutoCAD 命令统计魔幻球的实现过程--(3)
查看>>
dp学习笔记1
查看>>
newlisp debugger
查看>>
Java进阶02 异常处理
查看>>
图文介绍openLDAP在windows上的安装配置
查看>>
Heritrix 3.1.0 源码解析(十二)
查看>>