Springmvc整合Spring时,Spring配置文件扫描包与springmvc配置文件扫描包的不同(Spring父子容器)

        前面的几篇博客中,讲解到在ssm框架的集成过程中,我们发现sping的配置文件--applicationContext.xml文件负责扫描service层和dao层的注解,而spingmvc的配置文件---sping-mvc.xml文件负责扫描controller文件。

       为什么会这样处理呢?本篇文章就此进行深入研究(本文用到的图都是基于网上其他博主的博客),进入主题:

抛出一个问题:我们在搭建ssm框架时,声明式事务失效的原因?

         

             根本原因:由子容器扫描装配了@Service 注解的实例。

            spring的context是父子容器,由ServletContextListener 加载spring配置文件产生的是父容器,springMVC加载配置文件产生的是子容器,子容器对Controller进行扫描装配时装配了@Service注解的实例 (@Controller 实例依赖@Service实例),而该实例理应由父容器进行初始化以保证事务的增强处理,所以此时得到的将是原样的Service没有经过事务加强处理,故而没有事务处理能力。

(1)Spring作为父容器,SpringMVC作为子容器

     启动一个web.xml文件时,首先配置的是Spring容器的初始化加载的application文件,然后是SpringMVC的前端控制器(DispatchServlet),当配置完DispatchServlet后会在Spring容器中创建一个新的容器。其实这是两个容器,Spring作为父容器,SpringMVC作为子容器。

用一张图加以理解:

Springmvc整合Spring时,Spring配置文件扫描包与springmvc配置文件扫描包的不同(Spring父子容器)

 

         平时我们在项目中注入关系是这样的顺序(结合图来说):在Service中注入Dao(初始化自动注入,利用@Autowired),接着在Controller里注入Service(初始化自动注入,利用@Autowired),看图,这就意味这作为SpringMVC的子容器是可以访问父容器Spring对象的。

        那么问大家一个问题。要是反过来呢,你把Controller注入到Service中能行么? 
                 肯定是不行的啊!(如图,这也说明了父容器是不能调用子容器对象的)

        

       如果Dao,Serive,Controller要是都在Spring容器中,无疑上边的问题是肯定的,因为都是在一个bean里,一个容器中。

 

  (2 )现在放在具体的项目的配置文件进行研究以下几种配置情况

1、只在applicationContext.xml中配置如下


          <context:component-scan base-package="com" />
启动正常,但是任何请求都不会被拦截,简而言之就是@Controller失效,出现404错误。

Springmvc整合Spring时,Spring配置文件扫描包与springmvc配置文件扫描包的不同(Spring父子容器)

分析:此时的Spring容器中就会扫描到@Controller,@Service,@Reposity,@Component,但是spingmvc中没有controller

此时的图如下:

Springmvc整合Spring时,Spring配置文件扫描包与springmvc配置文件扫描包的不同(Spring父子容器)

      结合图去看,相当于他们都会放到大的容器中,而这时的SpringMVC容器中没有对象,没有对象就没有Controller,所以加载处理器,适配器的时候就会找不到映射对象,映射关系,因此在页面上就会出现404的错误。

 


2、只在springmvc.xml中配置

         <context:component-scan base-package="com" />

启动正常,请求也正常但是事物失效,也就是不能进行回滚

 

 


3、不正常情况下,Spring的配置文件扫描service层和dao层以及controller层,springmvc也扫描controller

      在applicationContext.xml和springmvc.xml中都配置

     <context:component-scan base-package="com" />
启动正常,请求正常,也是事物失效,不能进行回滚

 


4、正常情况下,Spring的配置文件扫描service层和dao层,springmvc扫描controller

      在applicationContext.xml中配置如下
<context:component-scan base-package="com.service" />
      在springmvc.xml中配置如下
<context:component-scan base-package="com.action" />

 

或者按最正确的配置applicationContext.xml,将Controller的注解排除掉 ,springmvc.xml,将Service注解给去掉 
此时启动正常,请求正常,事物也正常了。

配置如下图所示:

Springmvc整合Spring时,Spring配置文件扫描包与springmvc配置文件扫描包的不同(Spring父子容器)

Springmvc整合Spring时,Spring配置文件扫描包与springmvc配置文件扫描包的不同(Spring父子容器)

 

 

 

 

(3)关于sping与springmvc的一些疑问

1. 为什么使用Spring ?

    1). 方便解耦,简化开发

         通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。

    2). AOP编程的支持

         通过Spring提供的AOP功能,方便进行面向切面的编程,如性能监测、事务管理、日志记录等。

    3). 声明式事务的支持

    4). 方便集成各种优秀框架

    5). 降低Java EE API的使用难度

         如对JDBC,JavaMail,远程调用等提供了简便封装

2、如果不用Spring容器,直接把所有层放入SpringMVC容器中可不可以?


         当然可以,如果没有Spring容器,我们是可以把所有层放入SpringMVC的。单独使用这个容器是完全可以的,而且是轻量级的。


3、那么为什么我们在项目中还要联合用到Spring容器和SpringMVC容器? 

       Spring的扩展性,如果要是项目需要加入Struts等可以整合进来,便于扩展框架。如果要是为了快,为了方便开发,完全可以用SpringMVC框架。

 

4、事务为什么加在service层而不加在dao层

       

      首先理解 什么是事务?

             在数据库中,所谓事务是指一组逻辑操作单元即一组sql语句。当这个单元中的一部分操作失败,整个事务回滚,只有全部正确才完成提交。判断事务是否配置成功的关键点在于出现异常时事务是否会回滚。

        在javaEE中,web开发采用分层结构,Spring事务为业务逻辑进行事务管理,保证业务逻辑上数据的原子性。

事务得根据项目性质来细分:事务可以设置到三个层面(dao层、service层和web层)。 

第一:web层事务,这一般是针对那些安全性要求较高的系统来说的。例如电子商务网站。粒度小,一般系统用不着这么细。 

第二:service层事务,这是一常见的事务划分, 将事务设置在业务逻辑上,只要业务逻辑出错或异常就事务回滚。粒度较小,一般推荐这种方式。

第三:数据持久层数据务,也就是常说的数据库事务。这种事务在安全性方面要求低。就是给一个简单的增删改之类的操作增加事务操作。粒度大

给Service层配置事务,因为一个Service层方法操作可以关联到多个DAO的操作。在Service层执行这些Dao操作,多DAO操作有失败全部回滚,成功则全部提交。

事务分为业务事务和系统事务,业务事务也就是业务逻辑上操作的一致性,系统事务自然就是指真正的数据库事务,

Spring配置事务的是为了什么进行管理,当然是为业务逻辑进行事务管理,保证业务逻辑上数据的原子性;

Dao层是什么,数据访问层,是不应该包含业务逻辑的,这就是和Service层的不同;

Service层就是业务逻辑层,事务的管理就是为Service层上的保证。

 

 

5、以上是传统型SSM(spring+springmvc+mybatis)配置时,父类容器负责扫描装配service层和dao层,子类容器负责扫描装配controller层,但是在轻量化的javaEE开发中,我们可以不用spring框架,只是利用spinrgmvc+mybatis即可

        具体操作:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet。将原本应该放在父容器中的数据源、服务层、DAO层、事务的Bean、以及子容器controller的Bean都放在子上下文容器中,这样也可以实现事务。

详情见下一篇转载的博客。