springmvc 疑点(一) 事物处理失效
2015-12-29 17:54
483 查看
在项目中使用springmvc+spring+mybatis进行项目整合,出现了一个奇怪的现象,首先我来介绍一下项目中的配置,首先是spring-content.xml父容器的配置:
下面再给出datasource-context.xml的配置,也就是数据源和事物的配置,我们使用的是声明式事物处理:
在给出子容器配置文件:
走到这里,就可以在service中使用@Transactional进行事物管理,但是,在spring父上下文中,我们扫描了Controller和Service,在dispatcher-servlet.xml也即是子容器中我们同样扫描了这两者,那么问题就来了
因为我们的事物是在父容器中,而整个服务器在进行加载配置文件的时候呢,由于服务器启动时的加载配置文件的顺序为web.xml---spring-context.xml(Spring的配置文件)---servlet-context.xml(SpringMVC的配置文件),由于spring-context.xml配置文件中Controller会先进行扫描装配,此时service进行事务增强处理,得到的将是经过事务加强处理的Service,但是子容器继续扫描,由于事物管理在父容器中声明,自容器一旦扫描,就将service重新定义为没有事物的service
可能有点不理解啊,其实很简单,Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的.
也即是说,我们只要让父容器扫描service而自容器不扫描service就可以了,至于controller无关紧要,因为就算是子容器第二次进行扫描了controller,其中的service也是父容器扫描的,是具有事物的.
解决这个问题有两种方式了:
1.父容器不扫描controller,但扫描service,子容器扫描controller但不扫描service,那么父-子容器的配置就变成了:
2.第二种,父子都扫描,但是呢,子容器在扫描的时候,不可以扫描service,保留父扫描过的service.只改动自容器:
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd "> <!-- 添加注解驱动 --> <context:annotation-config /> <context:component-scan base-package="com.zefun.web,com.zefun.wechat,com.zefun.app"> </context:component-scan> <task:annotation-driven /> <context:property-placeholder location="classpath*:properties/dev/*.properties" /> <import resource="datasource-context.xml" /> <!-- <import resource="redis-cluster.xml"/> --> <import resource="redis-context.xml" /> <import resource="rabbitmq-context.xml" /> </beans>
下面再给出datasource-context.xml的配置,也就是数据源和事物的配置,我们使用的是声明式事物处理:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter"> <!-- 慢SQL记录,超过2秒日志输出 --> <property name="slowSqlMillis" value="2000" /> <property name="logSlowSql" value="true" /> </bean> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${jdbc.initialSize}" /> <property name="minIdle" value="${jdbc.maxIdle}" /> <property name="maxActive" value="${jdbc.maxActive}" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${jdbc.maxWait}" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <!-- 测试有效用的SQL Query --> <property name="validationQuery" value="SELECT 'x'" /> <!-- 连接空闲时测试是否有效 --> <property name="testWhileIdle" value="true" /> <!-- 获取连接时测试是否有效 --> <property name="testOnBorrow" value="true" /> <!-- 归还连接时是否测试有效 --> <property name="testOnReturn" value="true" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 分库分表较多的数据库,建议配置为false --> <property name="poolPreparedStatements" value="false" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 定期把监控数据输出到日志中 --> <!-- <property name="timeBetweenLogStatsMillis" value="300000" /> --> <property name="proxyFilters"> <list> <!-- 配置监控统计拦截的filters --> <ref bean="stat-filter" /> </list> </property> </bean> <!-- enable transaction demarcation with annotations --> <tx:annotation-driven /> <!-- PlatformTransactionMnager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- enable transaction annotation support --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- mybatis分页拦截器 --> <bean id="pageInterceptor" class="com.zefun.web.interceptor.PageInterceptor" /> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="plugins"> <list> <ref bean="pageInterceptor" /> </list> </property> <property name="mapperLocations" value="classpath*:sqlMap/*.xml"></property> </bean> <!-- mybatis mapper注入 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.zefun.web.mapper" /> </bean> </beans>
在给出子容器配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-lazy-init="true"> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" p:useCacheControlNoStore="true" p:cacheSeconds="0"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> </list> </property> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="50000000000000" /> <property name="maxInMemorySize" value="4096" /> <property name="defaultEncoding" value="GBK" /> </bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /> <bean class="com.zefun.common.utils.UTF8StringHttpMessageConverter" /> <bean class="org.springframework.http.converter.FormHttpMessageConverter" /> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> </list> </property> </bean> <!-- 添加注解驱动 --> <context:annotation-config /> <mvc:annotation-driven /> <context:component-scan base-package="com.zefun.web,com.zefun.wechat,com.zefun.app"> </context:component-scan> <mvc:default-servlet-handler /> <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <!-- 配置jsp路径前缀 --> <property name="prefix" value="/WEB-INF/view/"></property> <!-- 配置URl后缀 --> <property name="suffix" value=".jsp"></property> </bean> <!-- 配置session超时拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.zefun.web.interceptor.SessionInterceptor"> <property name="allowUrlPatterns"> <list> <value>/user/*.*</value> </list> </property> </bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.zefun.web.interceptor.AuthorityInterceptor"> <property name="allowUrlPatterns"> <list> <!-- 微信认证 --> <value>/</value> <!-- 微信认证 --> <value>/coreServlet</value> <!-- 登陆 --> <value>/user/login/*.*</value> <value>/user/logout/*.*</value> <!-- 微信相关api --> <value>/wechat/*.*</value> <!-- 微信会员端 --> <value>/memberCenter/*.*</value> <!-- 移动端员工 --> <value>/staff/*.*</value> <!-- 后台新增平台门店 --> <value>/storeinfo/action/addstore</value> <value>/mobile/test</value> <value>/sms/auth/callback</value> <value>/storeapply/*.*</value> <value>/storedetail/*.*</value> <value>/agentapply/*.*</value> <value>/agentdetail/*.*</value> <value>/wechat/common/*.*</value> </list> </property> </bean> </mvc:interceptor> </mvc:interceptors> </beans>
走到这里,就可以在service中使用@Transactional进行事物管理,但是,在spring父上下文中,我们扫描了Controller和Service,在dispatcher-servlet.xml也即是子容器中我们同样扫描了这两者,那么问题就来了
因为我们的事物是在父容器中,而整个服务器在进行加载配置文件的时候呢,由于服务器启动时的加载配置文件的顺序为web.xml---spring-context.xml(Spring的配置文件)---servlet-context.xml(SpringMVC的配置文件),由于spring-context.xml配置文件中Controller会先进行扫描装配,此时service进行事务增强处理,得到的将是经过事务加强处理的Service,但是子容器继续扫描,由于事物管理在父容器中声明,自容器一旦扫描,就将service重新定义为没有事物的service
可能有点不理解啊,其实很简单,Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc_dispatcher_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的.
也即是说,我们只要让父容器扫描service而自容器不扫描service就可以了,至于controller无关紧要,因为就算是子容器第二次进行扫描了controller,其中的service也是父容器扫描的,是具有事物的.
解决这个问题有两种方式了:
1.父容器不扫描controller,但扫描service,子容器扫描controller但不扫描service,那么父-子容器的配置就变成了:
<!-- 自动扫描组件,此处为父容器,这里要把 controller去除,他们是spring-context.xml中配置的,如果不去除会影响事务管理的。 --> <context:component-scan base-package="com.zefun.web,com.zefun.wechat,com.zefun.app"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
<!-- 此处为自容器,扫描所有的controller 但是不扫描service,否则事物管理会失效 --> <context:component-scan base-package="com.zefun.web,com.zefun.wechat,com.zefun.app"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> </context:component-scan>
2.第二种,父子都扫描,但是呢,子容器在扫描的时候,不可以扫描service,保留父扫描过的service.只改动自容器:
<context:component-scan base-package="com.zefun.web,com.zefun.wechat,com.zefun.app"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> </context:component-scan>
相关文章推荐
- java_集合
- 出现java.lang.NoClassDefFoundError: com/google/common/base/Charsets异常错误
- java 内存管理
- Java获取客户端的IP地址
- spring多数据源的配置-以及原理
- eclipse创建父子工程
- java中ArrayList使用remove注意事项
- 25个Java机器学习工具&库
- Java API —— IO流小结
- Spring Web MVC
- Java中getResourceAsStream的
- MVC框架详解--Servlet+JSP+JavaBean模式(MVC)开发复杂的web应用
- 关于struts页面数字的格式化
- Java基本排序算法
- 如何修改maven默认JDK版本
- eclipse修改项目名称或copy
- springMVC 整合测试 freemarker
- JAVA序列化的总结
- Castor实现XML与Java的互转
- Spring Boot学习笔记-SQL数据库使用