如何解决SpringMVC对包的扫描范围扩大后导致的事务配置不生效问题

今天就跟大家聊聊有关如何解决SpringMVC对包的扫描范围扩大后导致的事务配置不生效问题,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

创新互联服务项目包括柯桥网站建设、柯桥网站制作、柯桥网页制作以及柯桥网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,柯桥网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到柯桥省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

在整合新的ssh框架的时候,spring+springmvc+hibernate的时候发现一个问题,就是持久层在使用hibernateTemplate的时候,并不会自动实现事务的提交,SpringMVC对包的扫描范围扩大后,导致的事务配置不生效问题

首先配置的是Spring容器的初始化加载的application文件,然后是SpringMVC的前端控制器(DispatchServlet),当配置完DispatchServlet后会在Spring容器中创建一个新的容器

其实这是两个容器,Spring作为父容器,SpringMVC作为子容器

web.xml中对Spring的配置


  
    
      org.springframework.web.context.ContextLoaderListener
    
  

  
  
    contextConfigLocation
    classpath:/applicationContext.xml
  

  
  
  
    spring
    org.springframework.web.servlet.DispatcherServlet
    1
  
  
    spring
    /
  

applicationContext.xml使用AOP声明式事务配置


    
        
            
            
            
            
        
    
    
        
        
        
    

遇到的问题是:通过Hibernate执行save方法后,数据未能插入到DB中并且控制台也没有打印出SQL(控制台没有输出)

通过仔细排查,阅读网络文章后,发现问题出现在spring-servlet.xml中: 

上述配置的结果是:SpringMVC对Service和Dao的所有package进行了扫描装载

问题分析:

1、Spring与SpringMVC属于父子容器关系。框架启动时先启动Spring容器,而后启动SpringMVC容器。子容器可以访问父容器中的Bean,而父容器不能访问子容器中的Bean

2、由于SpringMVC在扫描时扩大了扫描范围,装载了@Service标识的类的实例,从而导致Controller层在注入Service时,实际注入的是子容器中的Service实例

3、事务被配置在父容器中,Spring父容器在装载Service时会同时应用事务配置,而SpringMVC只是单纯加载Service的实例

解决的办法如下

applicationContext.xml扫包排除掉Controller


    

springmvc.xml扫包只扫描controller


    

springmvc配置文件 use-default-filters="false",因为use-default-filters的值默认是true,也就是扫描全部的带有@Controller、@Service等注解的包了,加上之后则只扫描controller

进一步证明
打印容器管理的bean名称

我们使SpringMVC扫描Controller和Service,Spring扫描Service和DAO。

使用以下代码打印父子窗口管理的bean名称:

WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
        String[] definationBeanNames = webApplicationContext.getBeanNamesForAnnotation(Service.class);
        List names = new ArrayList(Arrays.asList(definationBeanNames));
        Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Controller.class));
        Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Repository.class));
        System.out.println("Spring 父容器管理的Bean:");
        for(String beanName : names){
            System.out.println(beanName);
        }

        webApplicationContext = RequestContextUtils.getWebApplicationContext(request);
        definationBeanNames = webApplicationContext.getBeanNamesForAnnotation(Service.class);
        names = new ArrayList(Arrays.asList(definationBeanNames));
        Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Controller.class));
        Collections.addAll(names, webApplicationContext.getBeanNamesForAnnotation(Repository.class));

        System.out.println("SpringMVC 子容器管理的Bean:");
        for(String beanName : names){
            System.out.println(beanName);
        }

如何解决SpringMVC对包的扫描范围扩大后导致的事务配置不生效问题

我们发现父子容器同时维护了Service层的类的实例,并且应该是两个独立的实例。

只使用子容器,而完全不使用父容器

现在我们测试另外一个场景

将web.xml中,注释掉ContextLoaderListener,修改配置为:


  
  
    spring
    org.springframework.web.servlet.DispatcherServlet
    
      contextConfigLocation
      classpath:/applicationContext.xml;/WEB-INF/spring-servlet.xml
    
    1
  
  
    spring
    /
  

移除了父容器,所有配置文件全部交由SpringMVC加载 。

打印结果如下:

如何解决SpringMVC对包的扫描范围扩大后导致的事务配置不生效问题

1、当事务交由Spring管理时,Spring负责管理session中事务的开启、关闭、flush等步骤,开发者只需调用例如save、update方法即可

2、当web项目框架中存在父子容器,且事务由父容器管理时,就应当注意SpringMVC对包的扫描范围并且只需扫描Controller组件。官方推荐:父子容器应当各执其责

3、如果子容器加载了Service的话,则在该实例上事务并不会生效。也就是Spring不会在service的方法被调用时自动开启事务

4、基于2中的前提:SpringMVC应只加载web相关配置(视图配置、Controller注解扫描),由Spring加载数据源、事务配置、Service和Dao注解扫描

看完上述内容,你们对如何解决SpringMVC对包的扫描范围扩大后导致的事务配置不生效问题有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注创新互联行业资讯频道,感谢大家的支持。


网页标题:如何解决SpringMVC对包的扫描范围扩大后导致的事务配置不生效问题
文章源于:http://ybzwz.com/article/iihohh.html