spring父子容器

Spring 和SpringMVC 的父子容器关系 Spring和SpringMVC做为Bean管理容器和MVC层的默认框架,已被众多WEB应用采用,而实际使用时,因为有了强大的注解功能,不少基于XML的配置方式已经被替代,可是在实际项目中,同时配置Spring和SpringMVC时会出现一些奇怪的异常,好比Bean被屡次加载,屡次实例化,或者依赖注入时,Bean不能被自动注入,可是明明你已经将该Bean注册了的。找缘由仍是要看问题的根源,咱们从容器提及。 在Spring总体框架的核心概念中,容器是核心思想,就是用来管理Bean的整个生命周期的,而在一个项目中,容器不必定只有一个,Spring中能够包括多个容器,并且容器有上下层关系,目前最多见的一种场景就是在一个项目中引入Spring和SpringMVC这两个框架,其实就是2个容器,Spring是根容器,SpringMVC是其子容器,而且在Spring根容器中对于SpringMVC容器中的Bean是不可见的,而在SpringMVC容器中对于Spring根容器中的Bean是可见的,也就是子容器能够看见父容器中的注册的Bean,反之就不行。理解这点很重要,由于这是一个规则,是Spring本身设定的,可是往下看,咱们会发现有些地方它并不默认使用这个规则。 当咱们使用注解时,对于Bean注册这个功能的实现就不须要在给每一个Bean配置XML了,只要使用统一的以下配置便可。 1 根据Spring提供的参考手册,该配置的功能是扫描默认包下的全部的@Component注解,而且自动注册到容器中,同时也扫描 @Controller@Service,@Respository这三个注解,他们是继承自@Component。 除了以上咱们使用的扫描配置,在项目中咱们常常见到的就是这个配置,其实有了以上的配置,这个是能够省略掉的。 还有一个SpringMVC相关的是配置,通过验证,这个是必需要配置的,由于它是和@RequestMapping结合使用的,这里补充下SpringMVC框架相关的知识点。 HandlerMapping,是SpringMVC中用来处理Request请求URL到具体Controller的,其自身也分红不少种类; HandlerAdapter,是SpringMVC中用来处理具体请求映射到具体方法的,其自身也分不少种类; @RequestMapping这个注解的主要目的就是对具体的Controller和方法进行注册,以方便HandlerMapping用来处理请求的映射。可是@RequestMapping须要结合使用才能生效。 好了,有了以上基础知识的铺垫,咱们看下如今这样的一个使用场景中,Spring与SpringMVC的容器冲突的缘由在那里! Spring配置文件applicationContext.xml,SpringMVC配置文件applicationContext-MVC.xml,这样项目中就有2个容器了,配置方式A,以下: applicationContext.xml中配置了,负责全部须要注册的Bean的扫描工做,applicationContext-MVC.xml中配置,负责springMVC相关注解的使用,启动项目发现,springMVC失效,没法进行跳转,开启log的DEBUG级别进行调试,发现springMVC容器中的请求好像没有映射到具体controller中; 配置方式B,以下: 为了快速验证效果,将扫描配置到applicationContext-MVC.xml中,重启后,验证成功,springMVC跳转有效。 要想查看具体缘由,翻看源码,从springMVC的DispatcherServlet开始看,在一个请求进来以后,发生了什么?漫长的查看以后,找到缘由,以下。 springMVC初始化时,会寻找全部当前容器中的全部@Controller注解的Bean,来肯定其是不是一个handler,而当前容器springMVC中注册的Bean中并无@Controller注解的,注意,上面说起的配置方式A,全部的@Controller配置的Bean都注册在Spring这个父容器中了,看代码。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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,代码以下: 1 2 3 protected boolean isHandler(Class beanType) { return AnnotationUtils.findAnnotation(beanType, Controller.class) != null; } 在配置方式B中,springMVC容器中包括了全部的@Controller注解的Bean,因此天然就能找到了。 以上是缘由,解决办法是什么?注意看initHandlerMethods()方法中,detectHandlerMethodsInAncestorContexts这个Switch,它主要控制从那里获取容器中的bean,是否包括父容器,默认是不包括的。因此解决办法是有的,即在springMVC的配置文件中配置HandlerMapping的detectHandlerMethodsInAncestorContexts属性为true便可(这里须要根据具体项目看使用的是哪一种HandlerMapping),让其检测父容器的bean。以下: 1 2 3 4 5 true 以上已经有了2种解决方案了,但在实际工程中,会包括不少配置,根据不一样的业务模块来划分,因此咱们通常思路是各负其责,明确边界,Spring根容器负责全部其余非controller的Bean的注册,而SpringMVC只负责controller相关的Bean的注册。第三种方案以下: Spring容器配置,排除全部@controller的Bean 1 2 3 SpringMVC容器配置,让其只包括@controller的Bean 1 2 3 我的比较推荐第三种方案。引伸一下,项目中使用事务的配置方案,也会在这种场景下失效,归根结底也是因为2个容器的可见性问题致使,能够结合具体问题按照上面的思路进行查找缘由! 引用一下海涛的博客中的一张图。 原图地址http://jinnianshilongnian.iteye.com/blog/1602617 转载开涛博客
相关文章
相关标签/搜索