当component-scan配置不当时,即便是正确和通过public接口等配置和使用事务管理,Spring事务管理器也不能生效。
由于web.xml中的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error.html</location> </error-page> </web-app> |
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中配置:
1 2 3 |
<context:component-scan base-package="com.example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> |
而在servlet-context中配置:
1 2 3 4 |
<context:component-scan base-package="com.example" > <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> </context:component-scan> |
此外,使用use-default-filters=false禁用默认行为也可以,这可以通过Spring源码证实:
<context:component-scan>会是通过org.springframework.context.config.ContextNamespaceHandler处理的:
ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理:
如果没有配置<context:component-scan>的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters是否为true来调用如下代码:
可以看到ClassPathBeanDefinitionScanner会自动注册对@Component、@ManagedBean、@Named注解的Bean进行扫描:
在前面的代码中我们可以看到ClassPathBeanDefinitionScanner会调用doScan:
doScan中调用了findCandidateComponents:
findCandidateComponents调用了isCandidateComponent:
isCandidateComponent会通过include-filter/exclude-filter来判断Bean是否有效:
即先通过exclude-filter进行黑名单过滤,再通过include-filter进行白名单过滤,否则默认排除。显然,问题的根源就在于use-default-filters默认为true,所以ClassPathBeanDefinitionScanner不仅仅扫描@Controller注解的Bean还会自动对其内部的@Component、@ManagedBean、@Named注解的Bean进行扫描。
转载时请保留出处,违法转载追究到底:进城务工人员小梅 » Spring事务管理器不能生效的原因之1-component-scan配置不当