此文几处有引用别人博客内容,如有侵权表示歉意!
1、spring 功能强大,其中最常用的功能 spring 是容器,是管理、产生bean的。专业术语是Ioc控制反转。spring 实例化也是通过new 关键字进行实例化,javabean 都有默认的构造器,不需要提供构造参数;spring 装载属性是通过xml文件调用bean中的setXXX方法装载相应属性值。
spring的两个核心接口BeanFactory和ApplicationContext,其中ApplicationContext 是BeanFactory类的子接口,他们都可以代表spring 容器,BeanFactory是负责配置、创建、管理Bean的,他的子接口ApplicationContext 俗称 上下文。
2、spring mvc 也是一个容器,相比spring容器相对较小,是spring 容器的子容器,spring mvc 管理Contorller bean的容器。spring mvc使用WebApplicationContext 类,该类扩展自ApplicationContext.
3、spring 容器和spring mvc 容器之间的关系:
spring 在整体框架的核心概念中,容器思想是其核心思想。 在一个项目应用中容器的个数可以是多个,spring中可以包含多个容器,其中容器之间存在层级关系。其中spring容器和spring mvc 容器就是两个容器,spring容器作为根容器,spring mvc 容器作为子容器,并且子容器可以看到父容器的Bean,父容器中不能看到子容器中的Bean.这是spring自己定义的规则。
①在 spring 配置xml文件时 配置组件扫描包,该功能扫描包下所有的@Component注解,并且自动注入到容器中,@Service、@Controller、@Repository继承自@Component。spring配置主要是为了扫描@Service和@Repository注解。配置如下:
<context:component-scan base-package="com.miniyu.test"/>
除了上诉的配置基础包扫描的方式,spring还提供了另外一种配置为<context:annotation-config base-package="con.miniyu.text"/>
。如果项目中 已经配置组件扫描可以不配置注解扫描。
②在spring mvc 配置xml文件时,需要配置组件扫描包<context:component-scan base-package=""/>
,主要是为了扫描@Controller,另外在spring mvc 配置文件中必须配置<mvc:annotation-driven/>
,注解驱动配置是为了与@requestMapping配合使用。
HandlerMapping是spring mvc中用来处理Request请求URL到具体Controller的,其自身也分成很多种类。
HandlerAdapter是spring mvc中用来处理具体请求映射到具体方法的,其自身也分很多中类
@RequestMapping 这个注解的主要目的就是对具体的Controller和方法进行注册,以方便HandlerMapping用来处理请求映射。但是@RequestMapping必须配合<mvc:annotation-driven/>
才能使用
在项目中为解决上诉容器之间的关系有如下三种方式:
- **(推荐)**在spring 配置扫描包指定@Service、@Repository所在包的路径,并在spring mvc 配置扫描包的指定@Controller所在包的路径,当然在spring配置扫描时也可以加入@Controller的扫描,但生成了此Bean会一直存储在spring容器中不进行使用,对资源照成浪费。总结:spring mvc容器中必须包含@Controller层级Bean,否则会请求会包404错误
- **(不推荐)**查看spring mvc 源码,在spring mvc 进行初始化时的执行:
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames =(this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (isHandler(
getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
方法**isHandler()**会判断当前的bean是否为Controller。
protected boolean isHandler(Class<?> beanType) {
return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;
}
由于initHandlerMotheds()这个方法中handlerMethodsInitialized()主要是控制在哪个容器中获取Bean,默认是不包含父容器,我们可以修改spring mvc 配置文件,配置HandlerMapping的detectHandlerMethodsInAncestorContexts属性为true,(这里注意要根据不同的handlerMapping进行不同的配置),让其检查父容器中的Bean.
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="detectHandlerMethodsInAncestorContexts">
<value>true</value>
</property>
</bean>
3.可以让spring 容器和spring mvc容器各司其职,spring 容器只负责非@Controller的扫描注册,spring mvc 容器只进行@Controller相关Bean 的扫描注册
--Spring容器配置,排除所有@controller的Bean
<context:component-scan base-package="com.fsnip.open">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
--SpringMVC容器配置,让其只包括@controller的Bean
<context:component-scan base-package="com.fsnip.open" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>