來張圖
圖片發(fā)自簡書App
前置
最近在開發(fā)一個web項目薪韩,集成hibernate值戳,且使用了OpenSessionInViewFilter劫流,發(fā)現(xiàn)spring的事務(wù)無效了庇楞。進行了如下配置:
spring上下文的applicationContext.xml
<context:component-scan base-package="com.github.mypost">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 事務(wù)管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 事務(wù)特性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="modify*" propagation="REQUIRED"/>
<tx:method name="edit*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="serviceMethod" expression="execution(* com.github.mypost.*.service..*+.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- annotation trasaction -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
springmvc的上下文配置webApplicationContext.xml
<context:component-scan base-package="com.github.mypost">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!--默認的注解映射的支持 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 攔截器 -->
<mvc:interceptors>
<bean class="com.github.mypost.commons.web.interceptor.BaseInterceptor"></bean>
</mvc:interceptors>
<!--視圖解釋器,根據(jù)不同的請求展示不同的View -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="viewResolvers">
<list>
<!--視圖解釋類 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
</bean>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<!-- 配置文件上傳解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8" />
</bean>
<mvc:view-controller path="/" view-name="index"/>
一直以為是使用了OpenSessionInViewFilter這個類庫的原因?qū)е碌氖。詈蟛虐l(fā)現(xiàn)是由于context:component-scan的use-default-filters導致的問題茴丰。
分析
- <context:component-scan>會交給org.springframework.context.config.ContextNamespaceHandler處理达皿;
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
- <context:component-scan>的use-default-filters默認true天吓,會交給ClassPathScanningCandidateComponentProvider進行掃描贿肩,看代碼:
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
可以看到默認ClassPathBeanDefinitionScanner會自動注冊對@Component峦椰、@ManagedBean、@Named注解的Bean進行掃描汰规。如果細心汤功,到此就找到問題根源了。
- 對exclude-filter溜哮,include-filter進行過濾
首先通過exclude-filter 進行黑名單過濾滔金;
然后通過include-filter 進行白名單過濾;
否則默認排除茂嗓。
結(jié)論
<context:component-scan base-package="com.github.mypost" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
那么這段代碼不僅僅對@Controller的注解Bean進行了掃描餐茵,而且對@Compoent注解也掃描了,子注解@Service述吸,@Repository也進行了掃描忿族,所以原來mvc的上下文的Bean被后面的bean替換了。所以如下解決方法:
- 如果不需要默認的蝌矛,則use-default-filters=“false”禁用掉道批。
- 直接寫明具體Controller包名稱。