javaweb sping项目中ContextConfigLocation中指定文件位置的设置规则

一般比较简单的项目,我们通常会用普遍简单的方式去配置web.xml比如:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>

但是实际中呢,一般公司是不会把dao和service直接以bean的方式放入application.xml中的,而是分别为dao和service弄一个新的配置文件,一般是以dao.xml和service.xml形式的名字弄一个配置文件,之后,再在web.xml中配置param-value

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:/main/config/*.xml</param-value>
</context-param>

采用通配符进行匹配。在公司中,肯定不可能像练习那样,所有东西都放到一起,可能自己认为,放到一起更方便看和管理,但是一个真正的公司项目是很大的,由多人分工,因此模块化,更易于管理和分配(刚好我们公司就采用如此配置,导致我有些疑惑,进而查证一番,拼凑了这个帖子以供大家参考)。
采用上述配置时,如果设置不当,则会引起双重加载controller层的bean类,如下图所示,是通用的设置SpringMvc上下文的方式,在这里插入代码片

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value>&nbsp; 默认
    </init-param>
    -->
    <load-on-startup>1</load-on-startup>
</servlet>

这样的话,当web.xml中的ContextConfigLocation采用上述的通配符方式设置文件加载位置的话,就会产生重复加载,虽然不至于出问题,但是其实并不是很妥当,至于原因,如下所述:

项目引入Spring MVC后,实际上已经存在两个Spring容器:

Spring 父容器:

1、管理业务层bean

2、Web容器启动时会触发ServletContextListener事件,从而启动了Spring父容器,从Spring容器启动顺序看,它是首先被启动的

3、它会扫描指定包名下面所有标注的类,并装配到容器,@Controller配置的bean也不例外。

4、Web层可以通过WebApplicationContextUtils.getWebApplicationContext(servletContext)取得Spring父容器

5、父容器不能访问子容器的bean

Spring MVC子容器:

1、管理Web层bean

2、DispatcherServlet是标准的Servlet,它设置了加载顺序为2,则未访问的情况下就会被Web容器实例化并执行init()初始化方法,方法中启动了Spring MVC子容器,因此从顺序上说,它是在父容器之后启动的。

3、它会扫描指定包名下面所有标注的类,并装配到容器,@Service等等配置的bean也不例外。

4、它的父亲是上面的父容器,这一点要注意。

5、可以通过WebApplicationContextUtils.getWebApplicationContext(servletContext,”org.springframework.web.servlet.FrameworkServlet.CONTEXT.” + DispatcherServlet的servlet名)取得

6、子容器可以访问父容器的bean

因此,两个容器重复扫描时,会在两个容器里出现相同类型但不同实例的Bean(形象地说就内存地址不一样),即Controller会在两个容器中实例化,但是父容器里的基本用不着(千万不要做从下而上逆向调用的设计),原因是从web容器进入来的请求首先会被DispatcherServlet捕捉到,而DispatcherServlet会交给Spring MVC容器处理,也就是说从子容器取出bean来处理,当子容器没有对应的bean时,则会从父容器找。

最后说下这种情况下的最佳实践:

假如某项目包名规划如下:

org.howsun.domain;

org.howsun.dao;

org.howsun.service;

org.howsun.web;

Spring父容器可以扫描到“org.howsun”,但是要排除掉@Controller bean:

XML/HTML code

<context:component-scan base-package="org.howsun">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

Spring MVC 子容器应该只扫描到“org.howsun.web”

XML/HTML code

<context:component-scan base-package="org.howsun.web"/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章