关于两个应用上下

《Spring实战(第四版)》中有一段代码如下:

package spittr.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import spittr.web.WebConfig;

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() { 
    	return new Class<?>[] { RootConfig.class }; 
    }
  @Override
  protected Class<?>[] getServletConfigClasses() {
  	return new Class<?>[] { WebConfig.class }; 
   }
  @Override
  protected String[] getServletMappings() { 
  	return new String[] { "/" };
  }
}

这其实也是实际项目中常用的一段代码,这里有两个非常相似的方法getRootConfigClasses()和getServletConfigClasses(),分别用于指定Root配置和Web配置。其中,Web配置类一般配置控制器、视图解析器等Web相关的Bean,而Root配置中定义业务逻辑、数据存取之类的其他Bean。在Spring官网文档里也提到了两个WebApplicationContext,一个是Servlet WebApplicationContext,一个是Root WebApplicationContext,前面代码的两个方法返回的配置类其实就是分别用于在这两个“上下文”中创建对象。(把Context看成“容器”要容易理解一些)一般来说,配置类通常还要通过组件扫描加载其他Bean,所以如果Bean、Component、Controller这些放错了包,或者组件扫描定义错误,有时候会出现运行时错误,有时候不会出现错误但是并不是希望的结果。因为Spring Bean装配都是运行时才装配,编译时不一定出错。当然,可以把所有的配置类也即所有的组件都放在一个context里,应该是可以的,无非是一个容器,所有东西都放在一起,就不会出现找不到的情况了,要是还找不到那肯定是代码本身有问题,漏掉了一些东西,而不会是Bean装配问题。的确,Spring文档里面也说,当Servlet在Servlet WebApplicationContext里找不到对象时,就会到Root WebApplicationContext里去找,甚至可以完全把所有东西都委托delegates给Root WebApplicationContext。既然如此,那为什么还要弄两个“上下文”呢?
这个问题我的理解是:Spring是模块化的,Spring的Core功能也就是依赖注入、切面这些功能,并不一定非得要和Spring MVC一起才能用,也许有些人已经习惯了其他框架,比如JSF,仍然可以使用Spring的其他功能的。这时,就没有Spring MVC也就没有Servlet WebApplicationContext了。如果用JSF作为框架,Spring的初始化是通过在Web.xml中定义的listener即org.springframework.web.context.ContextLoaderListener来进行的,这个类实现了Java EE定义的ServletContextListener接口,web容器在启动应用时会创建一个ServletContext用于Servlet与Web容器进行交互,然后调用 ServletContextListener接口的方法,ContextLoaderListener这个类就在这时初始化Spring的运行时环境(Context本意应该是运行时环境)这个Context就放在ServletContext里面,所以Servlet里可以访问得到。这个WebApplicationContext是Spring最初的Context,所以叫Root WebApplicationContext,这个时候连DispatcherServlet都还没有创建出来!同样道理,由于模块化的原因,为确保DispatcherServlet总是能够工作,org.springframework.web.servlet.DispatcherServlet这个类也没有完全依赖ContextLoaderListener创建的环境,它的构造函数有两个,当传递一个WebApplicationContext给它时,它就用这个“上下文”,这样就只有一个“上下文”,否则,它就自己创建一个,也就是Servlet WebApplicationContex,这样就有两个“上下文”。我觉得《Spring实战》中的建议不错的,把web相关的控制器、视图解析器、解码器、转换器这些放在Servlet WebApplicationContext里,web无关的放在Root WebApplicationContext这样结构性好一点,只是要注意配置组件扫描不要混乱。当然,包的结构清不清楚跟用几个上下文并没有多大关系,所有Bean都放在Root WebApplicationContext也行,其实都放在Servlet WebApplicationContex里也是可以的,因为一般来说包括Spring在内的框架都只有一个分派DispatcherServlet了,把Servlet创建的Contex实也就是整个Web应用程序的运行时环境也没什么不可。只是要知道一个Web应用可以有若干个Servlet的,所以要想把所有Bean放在一起,最好还是放Root WebApplicationContext里。
另外,刚才讲到了两个“上下文”,也说了只有一个“上下文”的可能性,但是从AbstractAnnotationConfigDispatcherServletInitializer这个类的继承树来看AbstractAnnotationConfigDispatcherServletInitializer->AbstractDispatcherServletInitializer->AbstractContextLoaderInitializer->WebApplicationInitializer,通过实现AbstractAnnotationConfigDispatcherServletInitializer类来初始化Spring Web应用程序的话,两个“上下文”都是创建好了的,要只创建一个“上下文”那只能是实现AbstractContextLoaderInitializer,但这时已经创建好的“上下文”仍然是Root WebApplicationContext,要想只有Servlet WebApplicationContex还不太容易,必须自己实现WebApplicationInitializer这个接口,要做的事太多。所以,两个“上下文”就两个“上下文”吧,明白上述道理后只需要知道把所有配置类都放在getRootConfigClasses、 getServletConfigClasses()其中之一是完全可以的,如果分开放,则需要知道ServletConfig里面的Bean可以通过注入引用RootConfig里面的Bean,反之则不行,因为构建Root WebApplicationContext的时候还没有Servlet更没有Servlet WebApplicationContex。而Spring的依懒注入在找不到Bean情况下就会创建一个,这样就有可能在程序代码里面只定义了一个Bean,编译不会出问题,但运行时,在两个上下文里面各有一个实例。本来只应用有一个的,现在多出一个来,程序运行起来当然达不到目的,这种错误很不好排查,跟踪调试看到的代码都是对头的,就是值不一样,呵呵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值