你的位置:首页 > Java教程

[Java教程]Spring IoC 学习 ——ApplicationContext 与 DispatcherServlet


 
最近在新项目中接触到 Spring MVC,在学习过程中对 Spring IoC 的配置产生了一些疑问。通过同事的指点,和查阅资料,现在初步对其做一点总结归纳:
 
 
在 Spring Web 应用中,有两种不同 IoC 容器的实现方式:ApplicationContext 与 WebApplicationContext。
 

其中,对于每一个 Web 应用,都只有唯一一个对应的 ApplicationContext。ApplicationContext 是由 ContextLoaderListener 来实现的,通过如下方式在 web.
<listener>   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param>   <param-name>contextConfigLocation</param-name>   <param-value>classpath:applicationContext.</param-value></context-param>


 
 
由于 ContextLoaderListener 实现了 ServletContextListener 接口,所以 ContextLoaderListener 会在 Web 容器启动时,默认执行它实现的方法,通过调用 ContextLoader.initWebApplicationContext() 自动装配 applicationContext.
 
 

WebApplicationContext 继承自 ApplicationContext,可以看做是 ApplicationContext 的一个子集。Spring Web 应用中每个 DispatcherServlet 都对应一个独立的 WebApplicationContext。 当ContextLoaderListener 和 DispatcherServlet 一起使用时,ContextLoaderListener 会先创建一个根 ApplicationContext,然后 DispatcherServlet 创建的 WebApplicationContext 则会绑定到根 ApplicationContext 之下。
web.
<servlet>   <servlet-name>spring-mvc</servlet-name>   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   <init-param>      <param-name>contextConfigLocation</param-name>      <param-value>classpath:spring-mvc-servlet.</param-value>   </init-param>   <load-on-startup>1</load-on-startup></servlet>



所以也就不难理解为什么在 ApplicationContext 中定义的 bean 可以在每个 WebApplicationContext 中使用。
通常为了将 Web 层模块与底层业务模块分离,提供更高细粒度的注入控制,在 WebApplicationContext 中只会定义 web 相关的模块,如 Controller, ViewResolver 或 handlers。(每个 DispatcherServlet 是相互独立的)
 


 
 
在上面我们提到,比较受推崇的做法是将不同的模块交由不同的 ApplicationContext 来管理。那么在 web.
<listener>   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param>   <param-name>contextConfigLocation</param-name>   <param-value>classpath:applicationContext.</param-value></context-param><servlet>   <servlet-name>spring-mvc</servlet-name>   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   <init-param>     <param-name>contextConfigLocation</param-name>     <param-value>classpath:spring-mvc-servlet.</param-value>   </init-param>   <load-on-startup>1</load-on-startup></servlet>


在 spring-mvc-servlet 中:
<context:component-scan base-package="com.zhao">    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/></context:component-scan>


在 applicationContext.
<context:component-scan base-package="com.shunra.vcat">  <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/></context:component-scan> 


 
 
但其实在实际使用中我们并不一定需要遵守这个条约。尤其是对于较小的项目,很多时候其实仅靠 DispatchServlet 就可以很好地管理我们的 IoC 容器。这种情况下我们仅需要在 web.
<servlet>   <servlet-name>spring-mvc</servlet-name>   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   <init-param>     <param-name>contextConfigLocation</param-name>     <param-value>classpath:spring-mvc-servlet.</param-value>   </init-param>   <load-on-startup>1</load-on-startup></servlet>


在 spring-mvc-servlet 中:
<context:component-scan base-package="com.zhao"></context:component-scan>



这样项目的配置看起来会简洁的多,而且统一放置的配置文件也会方便我们管理。
 
当然,Spring Framework 为我们提供了两种 ApplicationContext 自有他的道理,在以下一些情况中,我们还是需要对不同的 context 分别进行配置:
  • 应用中包含的多个 DispatcherServlet 需要共享某个业务操作时
  • 应用中包含的非 Spring servlets 需要注入某个业务操作时
 
所以在实际应用中,还是需要灵活思考,选择最合适的配置方式。不能总是期望能找到 silver bullet : )