exp:<aop:config/> 事务配置无效

当使用<aop:config/>进行事务配置时遇到无效问题,原因是StudentService未被Spring加载,而是被SpringMVC加载导致。解决方案是确保Spring启动时扫描到Service,或者显式配置bean以启用事务管理。Spring应先于SpringMVC启动,且Spring负责事务管理,SpringMVC处理请求。通过调整配置,使Spring扫描所有Service,避免在SpringMVC中处理事务。

配置如下:

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!-- AOP切入点设置 -->
<aop:config>
    <aop:pointcut id="dbService" expression="execution(* 
    com.qyd.sms.service.api.student.service.StudentService.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="dbService" />
</aop:config>

事务管理表现为无效,同时还有一个非常奇怪的现象:如果将com.qyd.sms.service.api.student.service.StudentService这个类,以<bean/>的形式显式的配置在配置文件中,事务管理就可以生效

不得其解,很幸运在网上找到了这个:spring+springMVC,声明式事务失效,原因以及解决办法,结合目前的情况(显式配置生效,注解不生效),以及这篇文章的内容,无效的原因应该是StudentService没有被spring加载,而是被springMVC加载,而srpingMVC加载时没有处理事务相关的部分,所以事务配置无效

有起色,去着重看一下项目的spring、springMVC的启动方式,

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:/META-INF/spring/spring-beans.xml</param-value>
</context-param>

<servlet>
    <servlet-name>qyd-sms-servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/META-INF/spring/spring-web.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>qyd-sms-servlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

spring是通过ContextLoaderListener启动,springMVC通过DispatcherServlet启动,经过验证,spring 先于 springMVC 启动(暂时将ContextLoaderListener启动的部分称为spring,表示spring核心的部分,而springMVC则是处理请求的部分)

当前配置:spring启动配置了事务管理,没有配置自动扫描,springMVC配置了全局的自动扫描

事务无效的原因分析:
spring启动时能够拿到事务管理信息,但是由于没有配置扫描配置,所以看不到通过注解注册的StudentService,也就无法进行StudentService的事务相关的处理

等到springMVC启动时,可以注册StudentService,但是没有事务管理相关的配置,也就不会做事务相关的处理(tip:我不会尝试在springMVC中加入事务管理配置,去试一下是否事务能生效,因为即使能生效,也不会采用这种做法,理解即可)

所以 只要在spring启动时看到注册StudentService,就能够进行事务相关的处理,事务就能够生效,这也解释了为什么显式配置一个bean就可以生效,通常我们不会去显式配置每一个service,所以spring配置扫描即可解决该问题

我的解决方案:

springMVC:

<mvc:annotation-driven/>
<context:component-scan base-package="com.qyd.sms.controller" />

spring:

<context:component-scan base-package="com.qyd.sms" >
    <context:exclude-filter type="annotation" 
    expression="org.springframework.stereotype.Controller"/>     
</context:component-scan>

配置文件如上,原因下面有说明。

说明:
经过测试,springMVC Controller的注册是不能放在spring启动时进行的,因为springMVC拿到注册的Controller信息后进行了的进一步处理,用于接收来访的请求

我更倾向于,spring,springMVC使用同一个bean容器,springMVC完全是基于spring来处理依赖注入的关系的,只不过springMVC做了额外的事

既然二者功能不同,对于配置文件的话,我比较倾向于根据功能完全分开,springMVC只负责了Controller层的注册的扫描,spring则负责其它所有。

<?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:aop="http://www.springframework.org/schema/aop" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm" 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-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd"> <!-- 使用缓存annotation 配置 --> <!-- <cache:annotation-driven cache-manager="ehCacheManager" /> --> <mvc:default-servlet-handler/> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> </list> </property> </bean> <bean id="masterSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName"> <value>${jdbc.driverClassName}</value> </property> <property name="url"> <value>${jdbc.url.master}</value> </property> <property name="username"> <value>${jdbc.username.master}</value> </property> <property name="password"> <value>${jdbc.password.master}</value> </property> <!-- 连接池最大使用连接数 --> <property name="maxActive"> <value>50</value> </property> <!-- 初始化连接大小 --> <property name="initialSize"> <value>1</value> </property> <!-- 获取连接最大等待时间 --> <property name="maxWait"> <value>60000</value> </property> <!-- 连接池最大空闲 --> <!-- <property name="maxIdle"> <value>20</value> </property> --> <!-- 连接池最小空闲 --> <property name="minIdle"> <value>30</value> </property> <property name="testWhileIdle"> <value>true</value> </property> <property name="validationQuery"> <value>select 1 from dual</value> </property> <!-- 自动清除无用连接 --> <property name="removeAbandoned"> <value>true</value> </property> <!-- 清除无用连接的等待时间 --> <property name="removeAbandonedTimeout"> <value>90</value> </property> <!-- 连接属性 --> <property name="connectionProperties"> <value>clientEncoding=UTF-8</value> </property> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis"> <value>60000</value> </property> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis"> <value>300000</value> </property> </bean> <bean id="slaveSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName"> <value>${jdbc.driverClassName}</value> </property> <property name="url"> <value>${jdbc.url.slave}</value> </property> <property name="username"> <value>${jdbc.username.slave}</value> </property> <property name="password"> <value>${jdbc.password.slave}</value> </property> <!-- 连接池最大使用连接数 --> <property name="maxActive"> <value>50</value> </property> <!-- 初始化连接大小 --> <property name="initialSize"> <value>1</value> </property> <!-- 获取连接最大等待时间 --> <property name="maxWait"> <value>60000</value> </property> <!-- 连接池最大空闲 --> <!-- <property name="maxIdle"> <value>20</value> </property> --> <!-- 连接池最小空闲 --> <property name="minIdle"> <value>30</value> </property> <property name="testWhileIdle"> <value>true</value> </property> <property name="validationQuery"> <value>select 1 from dual</value> </property> <!-- 自动清除无用连接 --> <property name="removeAbandoned"> <value>true</value> </property> <!-- 清除无用连接的等待时间 --> <property name="removeAbandonedTimeout"> <value>90</value> </property> <!-- 连接属性 --> <property name="connectionProperties"> <value>clientEncoding=UTF-8</value> </property> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis"> <value>60000</value> </property> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis"> <value>300000</value> </property> </bean> <bean id="dynamicDataSource" class="com.sjsemi.rrc.datasource.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="master" value-ref="masterSource"/> <entry key="slave" value-ref="slaveSource"/> </map> </property> <!-- 默认数据源 --> <property name="defaultTargetDataSource" ref="masterSource"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dynamicDataSource"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据库连接池 --> <property name="dataSource" ref="dynamicDataSource"/> <!-- 配置myBatis全局配置文件:mybatis-config.xml --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- 扫描entity包 使用别名 --> <property name="typeAliasesPackage" value="com.sjsemi.rrc.entity"/> <!-- 扫描sql配置文件:mapper需要的xml文件 --> <property name="mapperLocations" value="classpath*:com/sjsemi/rrc/mybatis/**/*Mapper.xml"/> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <value> helpDialect = oracle </value> </property> </bean> </array> </property> </bean> <context:component-scan base-package="com.sjsemi.rrc"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <task:annotation-driven scheduler="taskScheduler" mode="proxy"/> <task:scheduler id="taskScheduler" pool-size="10"/> <!-- 文件上传解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"/> </bean> <!-- 扫描basePackage下所有的接口类,根据对应的mapper.xml为其生成代理类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 注入sqlSessionFactory --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 给出需要扫描Dao接口包 --> <property name="basePackage" value="com.sjsemi.rrc.mybatis.mapper"/> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg> </bean> <!-- 配置事务管理器 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource"></property> </bean> <!-- 注解方式配置事务 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="append*" propagation="REQUIRED"/> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="creat*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="modify*" propagation="REQUIRED"/> <tx:method name="edit*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="repair" propagation="REQUIRED"/> <tx:method name="delAndRepair" propagation="REQUIRED"/> <tx:method name="get*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="select*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="load*" read-only="true"/> <tx:method name="search*" read-only="true"/> <tx:method name="datagrid*" read-only="true"/> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* com.sjsemi.rrc.service.impl.*.*(..))"/> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/> </aop:config> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="dataSourceAspect" class="com.sjsemi.rrc.datasource.DataSourceAspect"/> <aop:config> <aop:pointcut id="dataSourcePointcut" expression="execution(* com.sjsemi.rrc.service.impl.*.*(..))"/> <aop:advisor pointcut-ref="dataSourcePointcut" advice-ref="dataSourceAspect" order="1"/> </aop:config> </beans>
最新发布
10-11
你提供的是一份完整的 Spring XML 配置文件(通常是 `applicationContext.xml` 或类似名称),用于配置一个基于 **Spring + MyBatis + Druid 数据源 + 动态数据源路由 + 定时任务** 的 Java Web 应用。 下面我们逐部分详细解析这份配置的含义、作用以及潜在优化点,并指出其中的关键设计思想。 --- ### ✅ 1. 基本命名空间与 Schema 定义 ```xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm" 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="..."> ``` - 这些是 Spring 的各种命名空间,启用对应的功能模块: - `context`: 组件扫描(`@Component`, `@Service` 等) - `aop`: AOP 切面编程 - `tx`: 声明式事务管理 - `task`: 定时任务支持(`@Scheduled`) - `mvc`: Spring MVC 支持(虽然这里可能在 root context 中不必要) 📌 **注意**:`mvc` 和 `oxm` 在非 Web 层的 Spring 根上下文中可能是多余的,建议移除以减少干扰。 --- ### ✅ 2. 属性占位符加载(`PropertyPlaceholderConfigurer`) ```xml <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> </list> </property> </bean> ``` - 加载 `jdbc.properties` 文件中的数据库连接信息。 - 使用 `${...}` 占位符引用这些值(如 `${jdbc.url.master}`)。 🔧 **建议升级**:使用更现代的 `<context:property-placeholder />` 替代旧式 Bean。 ```xml <context:property-placeholder location="classpath:jdbc.properties"/> ``` --- ### ✅ 3. 主从数据源配置(Druid) #### 主库数据源: ```xml <bean id="masterSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> ... </bean> ``` #### 从库数据源: ```xml <bean id="slaveSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> ... </bean> ``` - 使用了阿里巴巴的 **Druid 连接池**,具备监控、防御 SQL 注入等功能。 - 配置了合理参数: - 最大活跃连接数:50 - 最小空闲连接:30 - 检测空闲连接:每分钟一次 - 清理废弃连接超时时间:90 秒 - 验证查询语句:`select 1 from dual`(Oracle) ✅ 合理适用于生产环境。 --- ### ✅ 4. 动态数据源路由(读写分离) ```xml <bean id="dynamicDataSource" class="com.sjsemi.rrc.datasource.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="master" value-ref="masterSource"/> <entry key="slave" value-ref="slaveSource"/> </map> </property> <property name="defaultTargetDataSource" ref="masterSource"></property> </bean> ``` - 自定义类 `DynamicDataSource` 实现了 `AbstractRoutingDataSource`,实现动态切换主从库。 - 通常结合 AOP,在 service 方法上通过注解指定使用哪个数据源(如 `@DataSource("slave")`)。 📌 这是实现 **读写分离** 的关键组件。 --- ### ✅ 5. JdbcTemplate & SqlSessionFactory 配置 #### JdbcTemplate ```xml <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dynamicDataSource"/> </bean> ``` - 提供便捷的 JDBC 操作能力。 #### SqlSessionFactory(MyBatis) ```xml <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dynamicDataSource"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="typeAliasesPackage" value="com.sjsemi.rrc.entity"/> <property name="mapperLocations" value="classpath*:com/sjsemi/rrc/mybatis/**/*Mapper.xml"/> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <value>helpDialect = oracle</value> </property> </bean> </array> </property> </bean> ``` - 将 MyBatis 与 Spring 整合。 - 扫描实体类别名、Mapper XML 文件。 - 集成分页插件 PageHelper(Oracle 分页支持)。 ✅ 正确且完整。 --- ### ✅ 6. Mapper 接口自动扫描 ```xml <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="basePackage" value="com.sjsemi.rrc.mybatis.mapper"/> </bean> ``` - 自动为所有接口生成代理对象并注册到 Spring 容器中。 - 不用手动编写实现类。 ✅ 推荐做法。 --- ### ✅ 7. SqlSessionTemplate ```xml <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg> </bean> ``` - 是线程安全的 `SqlSession` 包装类,可被多个 DAO 共享。 - 一般不需要显式定义,除非你在代码中直接注入它。 ⚠️ 如果没有手动依赖注入 `sqlSession`,可以删除此 bean。 --- ### ✅ 8. 事务管理器配置 ```xml <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <!-- 写操作:REQUIRED --> <tx:method name="get*" read-only="true"/> <!-- 读操作:只读 --> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* com.sjsemi.rrc.service.impl.*.*(..))"/> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/> </aop:config> ``` - 使用基于 XML 的事务切面控制。 - 对 `service.impl` 包下的方法进行事务增强。 - 写方法走 `REQUIRED`,读方法走 `read-only=true`,其他走 `SUPPORTS`。 ✅ 非常标准的事务配置。 📌 **但存在冲突风险**:同时使用了: - `<tx:annotation-driven />` - 手动 `<tx:advice>` + `<aop:config>` 👉 建议二选一。既然用了 `<tx:advice>` 显式定义规则,就不需要再用 `<tx:annotation-driven />`。 🔧 **推荐修改方案**: ```xml <!-- 删除 tx:annotation-driven --> <!-- 保留 tx:advice + aop:config 方式统一控制事务行为 --> ``` 否则可能导致重复织入或优先级混乱。 --- ### ✅ 9. AOP 切面:数据源切换 ```xml <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="dataSourceAspect" class="com.sjsemi.rrc.datasource.DataSourceAspect"/> <aop:config> <aop:pointcut id="dataSourcePointcut" expression="execution(* com.sjsemi.rrc.service.impl.*.*(..))"/> <aop:advisor pointcut-ref="dataSourcePointcut" advice-ref="dataSourceAspect" order="1"/> </aop:config> ``` - 使用自定义切面 `DataSourceAspect` 拦截 Service 方法,根据注解决定使用主库还是从库。 - 如:`@DataSource("slave")` → 走从库。 ✅ 实现读写分离的核心逻辑之一。 📌 注意 `order="1"` 表示该切面优先级高于事务切面(默认 order=0 的 `<tx:advice>`),确保**先确定数据源再开启事务**! ✅ 很重要!如果顺序颠倒,事务会绑定错误的数据源。 --- ### ✅ 10. 定时任务支持(`task`) ```xml <task:annotation-driven scheduler="taskScheduler" mode="proxy"/> <task:scheduler id="taskScheduler" pool-size="10"/> ``` - 启用 `@Scheduled` 注解功能。 - 使用大小为 10 的线程池执行定时任务(不再是单线程)。 ✅ 生产级配置,避免任务阻塞。 --- ### ✅ 11. 文件上传解析器 ```xml <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"/> </bean> ``` - 处理文件上传请求。 - 使用 Apache Commons FileUpload。 ⚠️ 注意:这个 `multipartResolver` 应该放在 **Spring MVC 的 Servlet Context** 中,而不是根容器中。否则可能无法生效。 📌 建议检查是否在 `dispatcher-servlet.xml` 或等效配置中也声明了它。 --- ### ❌ 总结:优点与改进建议 | 特性 | 是否已实现 | 建议 | |------|------------|-------| | 主从数据源配置 | ✅ | 良好 | | 动态数据源路由 | ✅ | 需确保 AOP 切面顺序正确 | | MyBatis 整合 | ✅ | 完整 | | Mapper 自动扫描 | ✅ | 推荐 | | 分页插件集成 | ✅ | Oracle 支持 OK | | 事务管理 | ⚠️ | 存在 `tx:annotation-driven` 与 `tx:advice` 冲突风险 | | 定时任务 | ✅ | 使用独立线程池,优秀 | | 读写分离 | ✅ | 通过 AOP 实现,合理 | | 组件扫描 | ✅ | 排除了 Controller,正确 | --- ### 🔧 推荐优化后的配置片段(关键部分) ```xml <!-- 推荐替代 PropertyPlaceholderConfigurer --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 删除 tx:annotation-driven --> <!-- 仅保留基于 XML 的事务切面 --> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="Throwable"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="get*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <aop:config expose-proxy="true"> <!-- 数据源切面优先级高 --> <aop:pointcut id="dataSourcePointcut" expression="@annotation(com.sjsemi.rrc.annotation.DataSource) || execution(* com.sjsemi.rrc.service.impl.*.*(..))"/> <aop:advisor advice-ref="dataSourceAspect" pointcut-ref="dataSourcePointcut" order="1"/> <!-- 事务切面 --> <aop:pointcut id="transactionPointcut" expression="execution(* com.sjsemi.rrc.service.impl.*.*(..))"/> <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut" order="2"/> </aop:config> ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值