【Tomcat】源码分析-ContextConfig,web.xml的解析
文章目录1. ContextConfig的基本流程2. ContextConfig的核心功能2.1 web-fragment.xml知识2.2 ServletContainerInitializers的实现类1. ContextConfig的基本流程同HostConfig一样,ContextConfig也是在Digester解析server.xml的时候添加到StandardContext上的监听
文章目录
1. ContextConfig的基本流程
同HostConfig一样,ContextConfig也是在初始化阶段由Digester解析server.xml的时候添加到StandardContext上的监听器,在后续的启动阶段,会触发调用这个之前创建的监听器:
初始化期间完成注册流程:
load() //catalina
->Digester digester = createStartDigester(); //catalina.load()
->digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/")); //catalina.createStartDigester()
->ruleSet.addRuleInstances(this); //Digester. addRuleSet()
-> digester.addRule(prefix + "Context",
new LifecycleListenerRule
("org.apache.catalina.startup.ContextConfig",
"configClass")); //ContextRuleSet.addRuleInstances()
->完成注册
ContextConfig 实现了LifecycleListener,就是一个监听器的实例:
public class ContextConfig implements LifecycleListener{
public void lifecycleEvent(LifecycleEvent event){
....
do many things here!
}
}
//定义前缀"Server/Service/Engine/Host/",后面会拼接这个前缀,作为容器对象的路径的前缀
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
//拼接表示容器的全路径信息
//Server/Service/Engine/Host/Context表示context需要添加一个ContextConfig 监听器
//最终加到context实现类StandardContext的集合属性lifecycleListeners中(该属性继承自父接口)
digester.addRule(prefix + "Context",
new LifecycleListenerRule
("org.apache.catalina.startup.ContextConfig",
"configClass"));
触发解析的流程,位于启动流程内:
startInternal() //StandardContext
->fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); //StandardContext.startInternal()
-> for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);//递归
} //LifecycleBase .fireLifecycleEvent()
->ContextConfig实例的fireLifecycleEvent()
-> if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart(); //消息类型为CONFIGURE_START_EVENT时
} //ContextConfig.fireLifecycleEvent()
调用 listener.lifecycleEvent(event);时,注意event=start
参见入参的源头代码: fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
2. ContextConfig的核心功能
ContextConfig主要是处理web应用的配置文件:
org.apache.catalina.startup.ContextConfig {
//监听器调用的入口
public void lifecycleEvent(LifecycleEvent event){
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
//根据入参,会走到该分支
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
}
...
}
protected synchronized void configureStart() {
// Called from StandardContext.start()
//调用此处方法
webConfig();
}
protected void webConfig() {
//xml解析器
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment(webXmlParser));
//创建web.xml格式的对象,此时属性都为空
WebXml webXml = createWebXml();
// 创建web.xml文件流
InputSource contextWebXml = getContextWebXmlSource();
//解析流,把内容封装到webXml
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
//处理web-fragment.xml文件,分多个步骤
//Step 1. 扫描/META-INF/lib/目录下的jar文件,如果在META-INF下含有web-fragment.xml文件,解析它
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
// Step 2. 确定确定这些xml片段的顺序
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
// Step 3. 处理ServletContainerInitializers的实现类
if (ok) {
processServletContainerInitializers();
}
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// Steps 4 & 5 都在processClasses里面,提前讲下
//Steps 4 处理/WEB-INF/classes 下的注解类和 @HandlesTypes matches
//Steps 5 处理 引入的JARs中的注解类和 @HandlesTypes matches
processClasses(webXml, orderedFragments);
}
if (!webXml.isMetadataComplete()) {
// Step 6. 将应用中的web.xml与orderedFragments进行合并,合并在WebXml类的merge方法中实现
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 7. 将应用中的web.xml与全局的web.xml文件(conf/web.xml和web.xml.default)进行合并
// Have to merge defaults before JSP conversion since defaults
// provide JSP servlet definition.
webXml.merge(defaults);
// Step 8. Convert explicitly mentioned jsps to servlets
if (ok) {
convertJsps(webXml);
}
// Step 9. 通过webXml对象,间接把web.xml构建成 Context对象
if (ok) {
configureContext(webXml);
}
}
1、扫描/META-INF/lib/目录下的jar文件,如果在META-INF下含有web-fragment.xml文件,解析它;
2、确定确定这些xml片段的顺序
3、处理ServletContainerInitializers的实现类
4、将应用中的web.xml与orderedFragments进行合并,合并在WebXml类的merge方法中实现
5、将应用中的web.xml与全局的web.xml文件(conf/web.xml和web.xml.default)进行合并
6、用合并好的WebXml来配置Context,这一步在处理servlet时,会为每个servlet创建一个wrapper,并调用addChild将每个wrapper作为context子容器,后续分析
2.1 web-fragment.xml知识
servlet 3.0可以将配置文件分散在多个jar包里面,而且还可以定义这些配置文件的顺序。分为绝对顺序和相对顺序,绝对顺序是通过absolute-ordering标签定义的:
<web-app>
<name>...</name>
<absolute-ordering>
<name>fragment1</name>
<name>fragment2</name>
</absolute-ordering>
</web-app>
还可以在web-fragment.xml里面通过before,after标签来定义这些配置文件的先后顺序,这里不再举例这步主要是根据顺序,将这些配置文件加到集合orderedFragments中。
2.2 ServletContainerInitializers的实现类
这也是servlet 3.0新增的特性,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,过滤掉不希望给 onStartup() 处理的类。
在onStartup方法中可以优先加载这些类,或者修改其中的方法等。这步主要是把这些类找到放到Set<ServletContainerInitializer> scis中,进而放入typeInitializerMap;
protected void processServletContainerInitializers() {
Set<ServletContainerInitializer> scis =
typeInitializerMap.get(type);
if (scis == null) {
scis = new HashSet<>();
typeInitializerMap.put(type, scis);
}
scis.add(sci);
2.3 web.xml文件加载的细节
只要知道了web.xml加载路径信息即可:
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)