接上文,我们发现添加spring 模块后会自动添加两个xml文件,其中applicationContext.xml是spring的配置文件,dispatcher-servlet.xml是springMVC的配置文件,我们在连个文件里面分别配置了controller的包扫描配置和其他的bean的扫描配置,那么为什么要把包分开扫描呢?
原来Spring 是父容器, Spring MVC是子容器, 子容器可以访问父容器的bean,父容器不能访问子容器的bean。
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置spring上下文环境 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- spring 配置Listener -->
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置springmvc前端控制器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation,springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<!-- 设置为负数或不设置时会在servlet第一次用到时调用init()方法 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
首先配置的是Spring容器的初始化加载的application文件,然后是SpringMVC的前端控制器(DispatchServlet),当配置完DispatchServlet后会在Spring容器中创建一个新的容器。其实这是两个容器,Spring作为父容器,SpringMVC作为子容器。
新建一个service来做几个测试:
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello! " + name;
}
}
@Controller
public class HelloController {
@Autowired
private HelloService helloService;
@ResponseBody
@RequestMapping("/hello/{name}")
public String sayHello(@PathVariable String name) {
return helloService.sayHello(name);
}
}
测试一:Spring加载所有除了Controller的bean,SpringMVC只加载Controller,结果访问正常。
<!--Spring applicationContext.xml-->
<context:component-scan base-package="com.spring.learn">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--SpringMvc dispatcher-servlet.xml-->
<context:component-scan base-package="com.spring.learn.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
测试二:Spring加载全部bean,SpringMVC不加载Bean,结果访问失败,报404错误。
<!--Spring applicationContext.xml-->
<context:component-scan base-package="com.spring.learn">
</context:component-scan>
<!--SpringMvc dispatcher-servlet.xml-->
无
分析一下原因: SpringMVC在匹配Controller与url的映射关系时只会在自己的上下文中查找Controller进行请求的处理。由于所有Controller的都在Spring容器中,SpringMVC找不到Controller,因此在页面上就会出现404的错误。
但是又一想不是说Spring 是父容器, Spring MVC是子容器, 子容器可以访问父容器的bean,父容器不能访问子容器的bean吗?那既然controller被注入到了父容器中为什么作为子容器的SpringMVC还会找不到Controller ???仔细想一想应该是SpringMVC在匹配Controller与url的映射关系时只会在自己的上下文中查找Controller进行请求的处理。
测试三:Spring不加载Bean,SpringMVC加载全部bean,结果访问正常。
<!--Spring applicationContext.xml-->
无
<!--SpringMvc dispatcher-servlet.xml-->
<context:component-scan base-package="com.spring.learn">
</context:component-scan>
但是,spring是父容器,springMVC是子容器,如果子容器进行扫描装配时装配了@Service注解的实例,而该实例理应由父容器进行初始化以保证事务的增强处理,所以此时得到的将是原样的Service(没有经过事务加强处理,故而没有事务处理能力)。
测试四:Spring只加载Controller,SpringMVC加载所有除了Controller的bean,结果访问失败,报404错误。
<!--Spring applicationContext.xml-->
<context:component-scan base-package="com.spring.learn.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--SpringMvc dispatcher-servlet.xml-->
<context:component-scan base-package="com.spring.learn">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
原因在测试二已经证明了,controller被注入到了父容器,SpringMVC找不到Controller。所以还是使用测试一的方法吧!