详解web.xml中元素的加载顺序

   最近在项目中遇到了启动时出现加载service注解注入失败的问题,后来经过不懈努力发现了是因为web.xml配置文件中的元素加载顺序导致的,那么就抽空研究了以下tomcat在启动时web.xml文件中元素的加载顺序,现在和大家分享。

     遇到这种问题的时候,一般看源码是最直接和最权威的获取答案的方式,根据tomcat架构设计Context的实现类是StandardContext,全称org.apache.catalina.core.StandardContext。看到其实现Lifecycle接口,我们在StandardContext中找到startInternal方法,下面给出我把暂时无用的代码去掉后的注释版源码:

 1 /**
 2 * Start this component and implement the requirements
 3 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 4 *
 5 * @exception LifecycleException if this component detects a fatal error
 6 *  that prevents this component from being used
 7 */
 8 @Override
 9 protectedsynchronized void startInternal() throwsLifecycleException {
10  //设置webappLoader 代码省略
11  
12  // Standard container startup 代码省略
13  
14   try{
15  
16     // Set up the context init params 
17     //初始化context-param节点数据
18     mergeParameters();
19  
20  
21     // Configure and call application event listeners
22     //配置和调用应用程序事件listeners 
23     if(ok) {
24       if(!listenerStart()) {
25         log.error("Error listenerStart");
26         ok = false;
27       }
28     }
29  
30     // Configure and call application filters
31     //配置和调用应用程序filters
32     if(ok) {
33       if(!filterStart()) {
34         log.error("Error filterStart");
35         ok = false;
36       }
37     }
38  
39     // Load and initialize all "load on startup" servlets
40     //加载和初始化配置在load on startup的servlets
41     if(ok) {
42       loadOnStartup(findChildren());
43     }
44  
45     // Start ContainerBackgroundProcessor thread
46     super.threadStart();
47   }finally{
48     // Unbinding thread
49     unbindThread(oldCCL);
50   }
51  
52 }

那我们接着归纳和整理一下代码:

  1.首先初始化context-param节点

  2.接着配置和调用listeners 并开始监听

  3.然后配置和调用filters filters开始起作用

  4.最后加载和初始化配置在load on startup的servlets

即元素加载顺序为:

context-param --> listeners --> filters --> servlet

注意:

  1.该加载顺序并不会受元素在web.xml文件中的位置的影响。

  2.但对于某类配置节而言,与它们出现的顺序是有关的。以 filter 为例,web.xml 中当然可以定义多个 filter,与 filter 相关的一个配置节是 filter-mapping,这里一定要注意,对于拥有相同 filter-name 的 filter 和 filter-mapping 配置节而言,filter-mapping 必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。

     (可以通过spring中@Order注解来进行初始化顺序的配置 )

     @Order注解规则:

    (1)order的值越小,优先级越高

     (2)order如果不标注数字,默认最低优先级,因为其默认值是int的最大值

     (3)该注解等同于实现Order接口的getOrder方法并返回数字

 
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
 
	/**
	 * The order value.
	 * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
	 * @see Ordered#getOrder()
	 */
	int value() default Ordered.LOWEST_PRECEDENCE;
 
}
	int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

接着让我们回忆一下web项目的启动顺序

    1.web容器读取web.xml配置文件,并首先读取<context-param>和<listener>两个节点

     2.容器创建一个ServletContext(servlet上下文),该web项目的所有部分都将共享这个上下文。

     3.容器将<context-param>转换为键值对,并交给servletContext.

     4.容器按照load on startup 中的启动顺序创建<listener>中的类实例,创建监听器。

关于load on startup

   load-on-starup元素在web应用启动的时候指定了servlet被加载的顺序,它的值必须是一个整数。

   如果它的值是一个负数或者是这个元素不存在,那么容器会在servlet被调用的时候加载这个servlet。

    如果值是正整数或零,容器再配置的时候就加载并初始化这个servlet,容器必须保证值小的先被加载。如果值相等,容器就可以自动选择先加载谁。

    正整数的值越小,启动该servlet的优先级越高。

加载spring:

    比如filter过滤器用到bean,但是加载顺序是:先加载filter后加载spring,则filter中spring初始化操作中的bean为null;

     所以,如果过滤器中要使用到 bean,可以将spring 的加载 改成 Listener的方式 :

<listener>  
        <listener-class>  
             org.springframework.web.context.ContextLoaderListener   
        </listener-class>  
</listener>

 或者从配置文件中获取

最终结论:

     web.xml的对应加载顺序是:context-->param-->listener-->filter-->servlet-->spring,而同类型的节点之间的实际程序调用的时候的顺序是根据对应的mapping的顺序进行调用的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值