`

Spring与SpringMVC的容器关系分析

    博客分类:
  • ssh
 
阅读更多
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了,只要使用统一的如下配置即可。
 
<context:component-scan base-package=“com.test" />
 
根据Spring提供的参考手册,该配置的功能是扫描默认包下的所有的@Component注解,并且自动注册到容器中,同时也扫描@Controller,@Service,@Respository这三个注解,他们是继承自@Component。
 
除了以上我们使用的扫描配置,在项目中我们经常见到的就是<context:annotation-config/>这个配置,其实有了以上的配置,这个是可以省略掉的。
还有一个SpringMVC相关的是<mvc:annotation-driven />配置,经过验证,这个是必须要配置的,因为它是和@RequestMapping结合使用的,这里补充下SpringMVC框架相关的知识点。
 
HandlerMapping,是SpringMVC中用来处理Request请求URL到具体Controller的,其自身也分成很多种类;
HandlerAdapter,是SpringMVC中用来处理具体请求映射到具体方法的,其自身也分很多种类;
@RequestMapping这个注解的主要目的就是对具体的Controller和方法进行注册,以方便HandlerMapping用来处理请求的映射。但是@RequestMapping需要结合<mvc:annotation-driven />使用才能生效。
 
好了,有了以上基础知识的铺垫,我们看下现在这样的一个使用场景中,Spring与SpringMVC的容器冲突的原因在那里!
 
Spring配置文件applicationContext.xml,SpringMVC配置文件applicationContext-MVC.xml,这样项目中就有2个容器了,配置方式A,如下:
applicationContext.xml中配置了<context:component-scan base-package=“com.test" />,负责所有需要注册的Bean的扫描工作,applicationContext-MVC.xml中配置<mvc:annotation-driven />,负责springMVC相关注解的使用,启动项目发现,springMVC失效,无法进行跳转,开启log的DEBUG级别进行调试,发现springMVC容器中的请求好像没有映射到具体controller中;
 
配置方式B,如下:
为了快速验证效果,将<context:component-scan base-package=“com.test" />扫描配置到applicationContext-MVC.xml中,重启后,验证成功,springMVC跳转有效。
 
要想查看具体原因,翻看源码,从springMVC的DispatcherServlet开始看,在一个请求进来之后,发生了什么?漫长的查看之后,找到原因,如下。
 
springMVC初始化时,会寻找所有当前容器中的所有@Controller注解的Bean,来确定其是否是一个handler,而当前容器springMVC中注册的Bean中并没有@Controller注解的,注意,上面提及的配置方式A,所有的@Controller配置的Bean都注册在Spring这个父容器中了,看代码。
 
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;
    }
 
在配置方式B中,springMVC容器中包括了所有的@Controller注解的Bean,所以自然就能找到了。
以上是原因,解决办法是什么?注意看initHandlerMethods()方法中,detectHandlerMethodsInAncestorContexts这个Switch,它主要控制从那里获取容器中的bean,是否包括父容器,默认是不包括的。所以解决办法是有的,即在springMVC的配置文件中配置HandlerMapping的detectHandlerMethodsInAncestorContexts属性为true即可(这里需要根据具体项目看使用的是哪种HandlerMapping),让其检测父容器的bean。如下:
 
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="detectHandlerMethodsInAncestorContexts">
            <value>true</value>
        </property>
    </bean>
 
以上已经有了2种解决方案了,但在实际工程中,会包括很多配置,根据不同的业务模块来划分,所以我们一般思路是各负其责,明确边界,Spring根容器负责所有其他非controller的Bean的注册,而SpringMVC只负责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>
 

 

个人比较推荐第三种方案。引申一下,项目中使用事务的配置方案,也会在这种场景下失效,归根结底也是由于2个容器的可见性问题导致,可以结合具体问题按照上面的思路进行查找原因!
分享到:
评论

相关推荐

    Spring和SpringMVC父子容器关系

    在Spring与SpringMVC的整合应用中,它们之间存在着一种特殊的父子容器关系,这种关系对于理解和优化应用的配置及管理非常重要。 首先,让我们深入理解Spring容器。Spring容器主要分为两种类型:ApplicationContext...

    Spring+SpringMVC+Mybatis资源课件

    1.2 企业级应用概述.pptx和2 Spring概述.pptx可能涵盖了Spring框架的基础概念,包括容器如何管理对象的生命周期以及如何通过XML或注解配置依赖关系。 SpringMVC是Spring框架的一部分,专门用于构建Web应用程序的...

    spring/springmvc/mybatis所需jar包

    - `mybatis-spring.jar`:Spring与MyBatis的整合库,提供Spring Bean与MyBatis的Mapper接口之间的绑定。 除了这些核心jar包,还有一些其他的依赖库,例如: - `log4j.jar` 或 `slf4j-api.jar + slf4j-log4j12.jar`...

    Spring和SpringMVC父子容器关系初窥(小结)

    在Spring父容器中注册的Bean对于SpringMVC容器中是可见的,而在SpringMVC容器中注册的Bean对于Spring父容器中是不可见的。这也就是子容器可以看见父容器中的注册的Bean,反之就不行。 三、配置文件分析 在Spring...

    Spring+SpringMVC+Mybatis框架项目整合

    在IT行业中,Spring、SpringMVC和Mybatis是三大非常重要的Java开发框架,它们的组合应用广泛用于构建企业级Web应用程序。"Spring+SpringMVC+Mybatis框架项目整合"是一个典型的后端技术栈,旨在提供高效、灵活且可...

    Spring,SpringMvc,Hibernate

    标题中的"Spring,SpringMvc,Hibernate"涉及到的是Java开发中常用的三个开源框架,它们在企业级Web应用开发中起着核心作用。以下是这三个框架的详细解释: **Spring框架**是Java开发中的一个全面的轻量级容器,它...

    SSM(Spring+springmvc+mybatis)项目实例.zip

    SSM(Spring、SpringMVC、MyBatis)是一个经典的Java web开发框架组合,它将Spring的核心容器、Spring MVC作为视图控制器以及MyBatis作为持久层框架集成在一起,为开发者提供了一种高效且灵活的开发方式。...

    SpringMVC-15 Spring整合SpringMVC

    通常情况下,开发时会将Spring配置文件和SpringMVC配置文件分离开,即类似于数据源、事务、整合其他框架、Service和DAO等配置放在Spring的原生配置...也具体说明了Spring的IoC容器与SpringMVC的IoC容器之间的关系……

    Spring+SpringMVC+Mybatis框架整合例子——亲测可用.zip

    在SSM整合中,Spring作为整体的容器,管理SpringMVC和Mybatis中的所有组件。SpringMVC负责处理HTTP请求,Mybatis则负责数据库操作。Spring通过Bean定义来管理和装配这些组件,包括SpringMVC的Controller、Service...

    Spring+SpringMvc+Mybatis学生管理系统

    1. **Spring框架**:Spring作为核心容器,负责管理应用中的对象(如Bean)及其依赖关系。它通过IoC(控制反转)和DI(依赖注入)来实现解耦,使得代码更易于测试和维护。此外,Spring还提供了AOP(面向切面编程)...

    mybatis3+spring+springMVC4整合jar包.rar

    标题 "mybatis3+spring+springMVC4整合jar包.rar" 描述的是一个整合了MyBatis 3、Spring 4 和 Spring MVC 4 的项目压缩包。这个压缩包通常用于快速搭建一个基于Java的Web开发环境,尤其是用于处理数据库操作和前端...

    spring+springMVC+mybatis jar包

    Spring是一个全面的后端应用程序开发框架,它提供了一个容器来管理对象的生命周期和依赖关系,称为IoC(Inversion of Control,控制反转)。Spring 3.1版本引入了更多的模块化,使得开发者可以根据需求选择使用哪些...

    基于SSM(Spring+SpringMVC+Mybatis)框架结构的java web应用开发案例

    在整合SSM框架时,通常会将Spring作为容器,管理SpringMVC和Mybatis的bean。SpringMVC负责接收和处理HTTP请求,然后调用由Spring管理的服务层(Service)对象,这些服务对象再通过Mybatis执行数据库操作。Mybatis的...

    ssm(Spring+SpringMVC+MyBatis)整合框架

    在SSM整合中,Spring作为整体的容器,负责管理所有组件,包括SpringMVC的Controller和MyBatis的SqlSessionFactory。SpringMVC处理HTTP请求,并将控制权交给Spring容器中的Service层进行业务逻辑处理。而MyBatis作为...

    SSM框架-详细整合教程(Spring+SpringMVC+MyBatis).pdf

    SSM框架,即Spring、SpringMVC和MyBatis的集成,是Java开发中常见的Web应用程序框架组合。这个框架集合提供了完整的后端解决方案,涵盖了依赖注入(DI)、面向切面编程(AOP)、模型视图控制器(MVC)以及持久层操作...

    Spring+SpringMVC+Mybatis框架整合JAR包

    Spring的核心容器包括Bean工厂和ApplicationContext,负责管理应用对象的生命周期和依赖关系。此外,Spring还提供了对数据访问(如JDBC、Hibernate、MyBatis)、Web应用、任务调度等多个领域的支持。 2. **...

    Spring+SpringMVC+Hibernate开发需要的jar

    SSH整合涉及到多个步骤,包括配置Spring的IoC容器、SpringMVC的DispatcherServlet、Hibernate的数据访问层,以及它们之间的交互。 在实际项目中,Spring会作为整个应用的基石,通过配置文件管理各组件的生命周期和...

    Spring-SpringMVC-Hibernate在maven下整合

    【Spring-SpringMVC-Hibernate在maven下整合】是一个常见的Java Web开发示例,它展示了如何在Maven项目结构中集成三个核心的Java企业级框架:Spring、SpringMVC和Hibernate。这些框架分别用于控制反转(IoC)、模型-...

    Spring+SpringMVC+MyBatis的简单搭建

    SSM框架,即Spring、SpringMVC和MyBatis的集成,是Java Web开发中常用的一种轻量级架构。这个框架组合提供了模型-视图-控制器(MVC)的架构模式,使得开发者能够更高效地处理业务逻辑、数据访问以及用户界面。下面将...

    spring+springMVC+mybatis简单实例

    在SSM实例中,Spring作为整个应用的容器,管理着其他组件的生命周期和对象之间的依赖关系。Spring通过XML配置或注解方式来定义bean,使得代码更加解耦和易于维护。 2. **SpringMVC**:SpringMVC是Spring框架的一个...

Global site tag (gtag.js) - Google Analytics