大道至简,知易行难
广阔天地,大有作为

Spring事务管理器不能生效的原因之1-component-scan配置不当

当component-scan配置不当时,即便是正确和通过public接口等配置和使用事务管理,Spring事务管理器也不能生效。
由于web.xml中的配置:

Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应servlet-context.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service exclude掉,此时得到的将是原样的无事务处理能力的Service。如果在Spring MVC配置文件中,不使用com.example.controller的前缀而是直接使用com.example,那么service、DAO层的Bean可能也重新加载了,但事务的AOP代理没有配置在Spring MVC配置文件中,从而造成新加载的Bean覆盖了老的Bean,造成事务失效。

因此,需要在applicationContext.xml中配置:

而在servlet-context中配置:

此外,使用use-default-filters=false禁用默认行为也可以,这可以通过Spring源码证实:
<context:component-scan>会是通过org.springframework.context.config.ContextNamespaceHandler处理的:

org.springframework.context.config.ContextNamespaceHandler

org.springframework.context.config.ContextNamespaceHandler

ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理:

org.springframework.context.annotation.ComponentScanBeanDefinitionParser

org.springframework.context.annotation.ComponentScanBeanDefinitionParser

如果没有配置<context:component-scan>的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters是否为true来调用如下代码:

org.springframework.context.annotation.ComponentScanBeanDefinitionParser.configureScanner

org.springframework.context.annotation.ComponentScanBeanDefinitionParser.configureScanner

可以看到ClassPathBeanDefinitionScanner会自动注册对@Component、@ManagedBean、@Named注解的Bean进行扫描:

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.registerDefaultFilters

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.registerDefaultFilters

在前面的代码中我们可以看到ClassPathBeanDefinitionScanner会调用doScan:

org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan

org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan

doScan中调用了findCandidateComponents:

org.springframework.context.annotation.ClasspathScanningCandidateComponentProvider.isCandidateComponent

org.springframework.context.annotation.ClasspathScanningCandidateComponentProvider.isCandidateComponent

findCandidateComponents调用了isCandidateComponent:

org.springframework.context.annotation.ClasspathScanningCandidateComponentProvider.findCandidateComponents

org.springframework.context.annotation.ClasspathScanningCandidateComponentProvider.findCandidateComponents

isCandidateComponent会通过include-filter/exclude-filter来判断Bean是否有效:

org.springframework.context.annotation.ClasspathScanningCandidateComponentProvider.isCandidateComponent2

org.springframework.context.annotation.ClasspathScanningCandidateComponentProvider.isCandidateComponent2

即先通过exclude-filter进行黑名单过滤,再通过include-filter进行白名单过滤,否则默认排除。显然,问题的根源就在于use-default-filters默认为true,所以ClassPathBeanDefinitionScanner不仅仅扫描@Controller注解的Bean还会自动对其内部的@Component、@ManagedBean、@Named注解的Bean进行扫描。

转载时请保留出处,违法转载追究到底:进城务工人员小梅 » Spring事务管理器不能生效的原因之1-component-scan配置不当

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址