Spring003--Spring事务管理(mooc)
Posted kaixinyufeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring003--Spring事务管理(mooc)相关的知识,希望对你有一定的参考价值。
Spring事务管理
一。事务回顾
1.1。什么是事务
事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。
异常情况发生,需要保证:【1】张三将钱转出,李四收到钱。【2】张三钱未成功转出,李四也未收到钱。
1.2。事务特性
事务有4大特性:原子性,一致性,隔离性,持久性。
1.2.1。原子性
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
//物理中强调原子是最小单位,不可分割。如张三向李四转钱,张三转出与李四转入这一组操作具有原子性,不可分割。
1.2.2。一致性
一致性指事务前后数据的完整性必须保持一致。
如:张三有2000元,李四有2000元。共计4000元。张三向李四转1000元,无论转账成功与否,合计一定要为4000元。保证数据前后一致性
1.2.3。隔离性
隔离性指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
如上图所示:事务A修改了张三的账户记录,此时事务B也修改张三的账户。此时会出现问题:重复修改或是事务A的修改被事务B覆盖。
所以需要保证事务A不受事务B干扰。
解决方案:数据库都会设置一些事务的隔离级别,可通过数据库的事务隔离级别控制一个事务不被另一个事务所干扰。
1.2.4。持久性
持久性是指一个事务一旦被提交,它对数据库数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响。
二。Spring事务管理的一组API
2.1。Spring接口
以上,PlatformTransactionManager事务管理器是用来真正管理事务的一个接口。它里面包含了像事务的提交,回滚等信息。
事务管理器 & 事务定义信息 & 事务具体运行状态之间是有联系的:
2.2。PlatformTransactionManager接口(平台事务管理器)
Spring为不同的持久层框架提供了不同的PlatformTransactionManager接口实现
2.3。TransactionDefinition定义事务隔离级别(隔离,传播,超时,只读)isolation
若不考虑事务隔离性,会引发安全问题如下:
1。脏读:
一个事务读取了另一个事务改写但还未提交的数据。如果这些数据被回滚,则读到的数据是无效的。导致查询结果不一致。
2。不可重复读:
一个事务读取了另一个事务改写又提交的数据,导致多次读取同一数据返回的结果有所不同。
3。幻读/虚读:
一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
隔离级别就是用来解决脏读,不可重复读及幻读问题的。
事务的4种隔离级别:级别低-》高(READ_UNCOMMITED->SERIALIZABLE)
注意:SERIALIZABLE是串行的,不可能出现并发情况。因此不会发生脏读,不可重复读及幻读情况。
Spring提供default默认隔离级别,即数据库用什么隔离级别,default就是什么隔离级别。
mysql默认采用REPEATABLE_READ隔离级别
oracle默认采用READ_COMMITED隔离级别
2.4。TransactionDefinition定义事务的传播行为(隔离,传播,超时,只读)
1。什么是事务的传播(propagation)行为?
事务的传播行为主要用来解决一些问题,
此时出现一个复杂情况:调用Service1.aaa()与Service2.bbb()才能够完成一个业务。
此时Service1.aaa()&Service2.bbb()均有事务,那么事务应用哪个?应用事务的传播行为。
事务的传播行为:用于解决业务层方法之间的调用,事务是如何进行传递的。
2。事务的7种传播行为(3类,重点记第1个)
说明:
1。PROPAGATION_REQUIRED:支持当前事务,如果存在,则用当前事务,不存在,则创建一个新的事务。
如上Service1.aaa()与Service2.bbb()。若Service2.bbb()定义传播行为是PROPAGATION_REQUIRED,
当Service1.aaa()有事务,则Service2.bbb()使用Service1.aaa()事务。即它们在同一事务中。要么均执行成功,要么均回滚。
当Service1.aaa()没有事务,则Service2.bbb()会创建一个新的事务。则Service2.bbb()事务回滚不会影响Service1.aaa
以下三种事务为同一组事务类型:
PROPAGATION_REQUIRED:支持当前事务,如果不存在则创建一个。
PROPAGATION_SUPPORTS:支持当前事务,如果不存在,则不使用事务。
PROPAGATION_MANDATORY:支持当前事务,如果不存在,则抛出异常。
相同点:
均为支持当前事务,即如果Service1.aaa有事务,则使用Service1.aaa的事务。
不同点:
PROPAGATION_REQUIRED:当Service1.aaa没有事务时,则新创建一个事务。
PROPAGATION_SUPPORTS:当Service1.aaa没有事务时,则不使用事务(即始Service.bbb有事务也不生效)。
PROPAGATION_MANDATORY:当Service1.aaa没有事务时,则抛出异常。
2。PROPAGATION_REQUIRES_NEW:如果有事务存在,则挂起当前事务,创建一个新的事务。
如上Service1.aaa()与Service2.bbb()。若Service2.bbb()定义传播行为是PROPAGATION_REQUIRES_NEW,
当Service1.aaa()有事务,则Service1.aaa事务会被挂起,Service2.bbb会新起一个事务。即它们不在同一事务中。
以下三种事务为同一组事务类型:
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
相同点:
嵌套的方法均不在同一个事务中,若当前(Service1.aaa)存在事务,则将该事务挂起。
不同点:
PROPAGATION_REQUIRES_NEW:创建一个新的事务。(Service2.bbb创建一个新的事务)
PROPAGATION_NOT_SUPPORTED:不使用事务。(Service2.bbb不使用事务)
PROPAGATION_NEVER:抛出异常。(Service2.bbb抛出异常)
3。PROPAGATION_NESTED:如果当前事务存在,则嵌套事务执行(复杂)
当Service1.aaa()有事务时,执行完Service1.aaa()时,会使用事务设置一个保存点(savepoint)。再执行Service2.bbb()时,若没有异常,则一起提交,
若有异常,则根据自定义设置,可以回滚到事务保存点,也可以回滚到最初始状态。可自己控制。
2.3。TransactionStatus接口(事务状态)
三。转账环境搭建
3.1。Spring支持两种方式事务管理:
1。编程式的事务管理
(1)。在实际应用中很少使用。
(2)。通过TransactionTemplate手动管理事务
2。使用XML配置声明式事务
(1)。开发中推荐使用(代码侵入性最小)
(2)。Spring的声明式事务是通过AOP实现的。
3.2。转账环境准备
1。数据库准备
2。创建Web项目
引入相应的JAR包及配置文件。
JAR包:
(1)。连接数据库需要引入数据库驱动包,连接池(c3p0)相关JAR包。
(2)。Spring相关jar包
(3)。日志包
配置文件:
(1)。log4j.properties:日志记录
(2)。jdbc.properties:数据库连接配置文件
(3)。applicationContext.xml:Spring核心文件
创建package,写业务逻辑:
(1)定义interface/AccountService:业务层接口
(2)定义实现类/AccountServiceImpl:业务层实现类,调用DAO层
(3)操作数据库Dao层接口
(4)DAO层接口实现类
(5)Spring完成一些相关配置
【1】。连接数据库(引入外部属性文件)& c3p0连接池
【2】配置业务层类
【3】配置DAO类:在DAO里面直接注入连接池即可。只要给它创建连接池,它就会为我们创建JDBC的模版。
此时即可以在Dao的实现类中进行编写,操作数据库。
(6)创建测试类
模拟service实现类异常,依然写入成功。扣钱与加钱操作应在同一个事务中。需要进行相应的事务管理。
四。Spring的编程式事务管理
1。在Spring核心配置文件加入代码:
上述:
真正进行事务管理的类是transactionManager,所以需要将真正事务管理的类给我们的transactionTemplate(模版)。
将底层代码简化。(<property name="transactionManager" ref="transactionManager">)
编程式事务管理:就是需要我们在使用事务时手动编写代码。
2。业务层类需要进行事务管理AccountServiceImpl。加入代码
3。Spring配置文件注入事务管理模版
4。业务层类AccountServiceImpl,转出与转入应该在一个事务当中。
修改如下所示:
再执行测试类进行测试。发现转入与转出被绑订到一个事务中,要么全部成功,要么全部失败。
五。Spring的声明式事务管理
Spring声明式事务管理是基于AOP的思想完成的。即再需要执行转账前与转账后需要做一些事情,正是AOP思想。
1。引入AOP及AOP联盟的包。(AOP思想本身不是Spring提出的,而是由AOP联盟提出的)
2。创建声明式事务测试类:(web/service/dao同原始)
3。Spring核心配置文件配置事务管理器
5.1。声明式事务管理方式一:基于TransactionProxyFactoryBean方式
配置业务层代理,即可以对业务层进行增强。(spring传统方式TransactionProxyFactoryBean事务代理工厂类)
说明:
1。业务层代理是对service 进行增强。
2。代理类对目标类要产生代理增强,如何增强,即对事务的一个增强。
3。事务管理器真正管理事务。如何管理,定义事务的一些属性,如传播行为,隔离级别等。
若属性增加readOnly,则代表事务为只读的,不能被修改等操作。一旦修改则抛出异常(上述转账)
若属性增加+Exception(+具体异常名称),则上述先转出操作成功执行,异常,后续转入操作不执行。
此时业务层不需要修改,因为采用的是AOP的思想。
需要修改点。测试类:(不能注入service,因为service是没有被增强的,不进行事务管理的。需要注入service代理类,才可以进行事务管理)
总结:
上述不建议使用,因为每增加一个Service就要配置相应的TransactionProxyFactoryBean,这样使得XML旁大。不便于维护。
5.2。声明式事务管理方式二:基于AspectJ的XML方式(公司应用)
AspectJ是Spring在进行AOP开发中,为了简化AOP编程的一个内容。AspectJ本身是一个开源的第三方的一个AOP开发框架。
1。引入AspectJ相应JAR开发包,包括AspectJ和Spring&AspectJ整合包。
2。只需要修改spring配置文件,而不需要修改其他。
(上述方式一需要修改测试类service注入为代理注入,此处不需要。为自动代理。类生成过程中本身就增强了,即为代理对象。不需要再注入代理对象。)
<tx_attributes>属性有如下配置:
项目实例:--主从分离
Step0:读取数据源属性文件。在Spring配置文件中引入属性文件
1 <util:properties id="baseConfig" location="classpath:config.properties" /> 2 <util:properties id="imConfig" location="classpath:important.properties"/>
在属性文件config.properties中配置相应数据源连接信息
【1】。config.properties定义
1 jdbc_trace.master01.url=${jdbc_url} 2 jdbc_trace.slave01.url=${jdbc_url}
【2】。pom.xml中配置mysql数据源
1 <profiles> 2 <profile> 3 <id>development</id> 4 <activation> 5 <activeByDefault>true</activeByDefault> 6 </activation> 7 <properties> 8 <!--mysql数据源配置--> 9 <jdbc_url><![CDATA[jdbc:mysql://192.168.x.x:3306/dbName]]></jdbc_url> 10 <!--mysql第二个数据源配置--> 11 <jdbc_bigdata_url><![CDATA[jdbc:mysql://192.168.y.y:3306/dbName]]></jdbc_bigdata_url> 12 <jdbc_username>ocsuser</jdbc_username> 13 <jdbc_password>ocspasswd</jdbc_password> 14 </properties> 15 </profile> 16 </profiles>
【3】。important.properties定义
1 jdbc_trace.master01.username=mysql_rw 2 jdbc_trace.master01.password=mysql_pwd 3 jdbc_trace.slave01.username=mysql_rw 4 jdbc_trace.slave01.password=mysql_pwd
Step1:定义JDBC数据源
【1】主库dataSource,配置c3p0连接池
1 <!-- masterDataSource --> 2 <bean id="master01DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 3 <property name="driverClass" value="com.mysql.jdbc.Driver"/> 4 <property name="jdbcUrl" value="#{baseConfig[\'jdbc_trace.master01.url\']}"/> 5 <!--<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/escort" />--> 6 <property name="user" value="#{imConfig[\'jdbc_trace.master01.username\']}"/> 7 <property name="password" value="#{imConfig[\'jdbc_trace.master01.password\']}"/> 8 <property name="minPoolSize" value="5"/> 9 <property name="maxPoolSize" value="20"/> 10 <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> 11 <property name="maxIdleTime" value="300"/> 12 <property name="acquireIncrement" value="2"/> 13 <property name="maxStatements" value="1000"/> 14 <property name="initialPoolSize" value="2"/> 15 <property name="idleConnectionTestPeriod" value="240"/> 16 <property name="acquireRetryAttempts" value="30"/> 17 18 <!--定义所有连接测试都执行的测试语句,比默认配置效率高--> 19 <property name="preferredTestQuery" value="select 1" /> 20 <!--每过一段时间检查所有连接池中的空闲连接。【单位为秒】Default 0 (0代表不探测) 21 它保证连接池会每隔一定时间对空闲连接进行一次测试, 22 从而保证有效的空闲连接能每隔一定时间访问一次数据库 23 --> 24 <property name="IdleConnectionTestPeriod" value="55"/> 25 <!-- 因为弹性数据库 链接1分钟内不活动会被弹性数据库回收,所以配置如下两个参数 --> 26 <!--如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。--> 27 <property name="testConnectionOnCheckin" value="true" /> 28 <!--因性能消耗大请只在需要的时候使用它。 29 如果设为true那么在每个connection提交的时候都 将校验其有效性。 30 建议使用 idleConnectionTestPeriod或automaticTestTable 31 等方法来提升连接测试的性能。默认为false; 32 --> 33 <property name="testConnectionOnCheckout" value="true" /> 34 </bean>
【2】从库dataSource,配置c3p0连接池
1 <!-- slaveDataSource --> 2 <bean id="slave01DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 3 <property name="driverClass" value="com.mysql.jdbc.Driver"/> 4 <property name="jdbcUrl" value="#{baseConfig[\'jdbc_trace.slave01.url\']}"/> 5 <!--<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/escort" />--> 6 <property name="user" value="#{imConfig[\'jdbc_trace.slave01.username\']}"/> 7 <property name="password" value="#{imConfig[\'jdbc_trace.slave01.password\']}"/> 8 <property name="minPoolSize" value="5"/> 9 <property name="maxPoolSize" value="20"/> 10 <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> 11 <property name="maxIdleTime" value="300"/> 12 <property name="acquireIncrement" value="2"/> 13 <property name="maxStatements" value="1000"/> 14 <property name="initialPoolSize" value="2"/> 15 <property name="idleConnectionTestPeriod" value="240"/> 16 <property name="acquireRetryAttempts" value="30"/> 17 18 <!--定义所有连接测试都执行的测试语句,比默认配置效率高--> 19 <property name="preferredTestQuery" value="select 1" /> 20 <!--每过一段时间检查所有连接池中的空闲连接。【单位为秒】Default 0 (0代表不探测) 21 它保证连接池会每隔一定时间对空闲连接进行一次测试, 22 从而保证有效的空闲连接能每隔一定时间访问一次数据库 23 --> 24 <property name="IdleConnectionTestPeriod" value="55"/> 25 <!-- 因为弹性数据库 链接1分钟内不活动会被弹性数据库回收,所以配置如下两个参数 --> 26 <!--如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。--> 27 <property name="testConnectionOnCheckin" value="true" /> 28 <!--因性能消耗大请只在需要的时候使用它。 29 如果设为true那么在每个connection提交的时候都 将校验其有效性。 30 建议使用 idleConnectionTestPeriod或automaticTestTable 31 等方法来提升连接测试的性能。默认为false; 32 --> 33 <property name="testConnectionOnCheckout" value="true" /> 34 </bean>
【3】引入主库与从库自定义的dataSource(c3p0连接池)
1 <!-- 定义数据源,使用自己实现的数据源 --> 2 <bean id="dataSource" class="com.common.datasource.DynamicDataSource"> 3 <!-- 设置多个数据源 --> 4 <property name="targetDataSources"> 5 <map key-type="java.lang.String"> 6 <!-- 这个key需要和程序中的key一致 --> 7 <entry key="master" value-ref="master01DataSource"/> 8 <entry key="slave" value-ref="slave01DataSource"/> 9 </map> 10 </property> 11 <!-- 设置默认的数据源,这里默认走写库 --> 12 <property name="defaultTargetDataSource" ref="master01DataSource"/> 13 </bean>
注意:
DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全。由DynamicDataSourceHolder完成。
自定义DynamicDataSource(com.common.datasource.DynamicDataSource)如下所示:
1 package com.common.datasource; 2 3 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 4 5 /** 6 * 定义动态数据源,实现通过集成spring提供的AbstractRoutingDataSource,只需要实现determineCurrentLookupKey方法即可 7 * 8 * 由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。 9 */ 10 public class DynamicDataSource extends AbstractRoutingDataSource { 11 12 @Override 13 protected Object determineCurrentLookupKey() { 14 // 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key 15 return DynamicDataSourceHolder.getDataSourceKey(); 16 } 17 }
DynamicDataSourceHolder.java
1 package com.common.datasource; 2 3 /** 4 * 使用ThreadLocal技术来记录当前线程中的数据源的key 5 */ 6 public class DynamicDataSourceHolder { 7 //写库对应的数据源key 8 private static final String MASTER = "master"; 9 10 //读库对应的数据源key 11 private static final String SLAVE = "slave"; 12 13 //使用ThreadLocal记录当前线程的数据源key 14 private static final ThreadLocal<String> holder = new ThreadLocal<String>(); 15 16 /** 17 * 设置数据源key 18 * @param key 19 */ 20 public static void putDataSourceKey(String key) { 21 holder.set(key); 22 } 23 24 /** 25 * 获取数据源key 26 * @return 27 */ 28 public static String getDataSourceKey() { 29 return holder.get(); 30 } 31 32 /** 33 * 标记写库 34 */ 35 public static void markMaster(){ 36 putDataSourceKey(MASTER); 37 } 38 39 /** 40 * 标记读库 41 */ 42 public static void markSlave(){ 43 putDataSourceKey(SLAVE); 44 } 45 }
Step2:定义事务管理器(必须),事务管理器才是真正管理事务。
1 <!-- 定义事务管理器--> 2 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 3 <property name="dataSource" ref="dataSource"/> 4 </bean>
Step3:配置事务通知(增强:如事务隔离级别及传播行为,只读等)
1 <!-- 对事务管理器进行增强--> 2 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 3 <tx:attributes> 4 <tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/> 5 <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/> 6 <tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/> 7 <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/> 8 <tx:method name="execute*" propagation="REQUIRED" rollback-for="Exception"/> 9 <tx:method name="do*" propagation="REQUIRED" rollback-for="Exception"/> 10 <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/> 11 <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> 12 <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> 13 <tx:method name="select*" propagation="SUPPORTS" read-only="true"/> 14 <tx:method name="query*" propagation="SUPPORTS" read-only="true"/> 15 </tx:attributes> 16 </tx:advice>
Step4:配置切面(即事务应用范围)
1 <!-- 定义AOP切面处理器 --> 2 <bean class="com.common.datasource.DataSourceAspectTx" id="dataSourceAspect"> 3 <!-- 指定事务策略 --> 4 <property name="txAdvice" ref="txAdvice"/> 5 <!-- 指定slave方法的前缀(非必须) --> 6 <property name="slaveMethodStart" value="query,find,get,select"/> 7 </bean> 8 <aop:config expose-proxy="true" proxy-target-class="true"> 9 <!-- 定义切面,所有的service的所有方法 --> 10 <aop:pointcut id="txPointcut" 11 expression="execution(* com.dao..*.service..*.*(..)) 12 or execution(* com.service..*.*(..)) 13 or execution(* com.tx.service..*.*(..)) 14 or execution(* com.common.service.impl.BaseServiceImpl.*(..))"/> 15 <!-- 应用事务策略到Service切面 --> 16 <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/> 17 <!-- 将切面应用到自定义的切面处理器上,-9999保证该切面优先级较高执行 --> 18 <aop:aspect ref="dataSourceAspect" order="-9999"> 19 <aop:before method="before" pointcut-ref="txPointcut"/> 20 </aop:aspect> 21 </aop:config>
自定义AOP切面处理器DataSourceAspectTx.java
1 package com.common.datasource; 2 3 import org.apache.commons.lang3.StringUtils; 4 import org.aspectj.lang.JoinPoint; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource; 8 import org.springframework.transaction.interceptor.TransactionAttribute; 9 import org.springframework.transaction.interceptor.TransactionAttributeSource; 10 import org.springframework.transaction.interceptor.TransactionInterceptor; 11 import org.springframework.util.PatternMatchUtils; 12 import org.springframework.util.ReflectionUtils; 13 14 import java.lang.reflect.Field; 15 import java.util.ArrayList; 16 import java.util.List; 17 import java.util.Map; 18 19 /** 20 * AOP切面处理器 21 */ 22 public class DataSourceAspectTx { 23 private final Logger logger = LoggerFactory.getLogger(DataSourceAspectTx.class); 24 private List<String> slaveMethodPattern = new ArrayList<String>(); 25 26 private static final String[] defaultSlaveMethodStart = new String[]{ "query", "find", "get" }; 27 28 private String[] slaveMethodStart; 29 30 /** 31 * 读取事务管理中的策略 32 */ 33 @SuppressWarnings("unchecked") 34 public void setTxAdvice(TransactionInterceptor txAdvice) throws Exception { 35 if (txAdvice == null) { 36 // 没有配置事务管理策略 37 return; 38 } 39 //从txAdvice获取到策略配置信息 40 TransactionAttributeSource transactionAttributeSource = txAdvice.getTransactionAttributeSource(); 41 if (!(transactionAttributeSource instanceof NameMatchTransactionAttributeSource)) { 42 return; 43 } 44 //使用反射技术获取到NameMatchTransactionAttributeSource对象中的nameMap属性值 45 NameMatchTransactionAttributeSource matchTransactionAttributeSource = (NameMatchTransactionAttributeSource) transactionAttributeSource; 46 Field nameMapField = ReflectionUtils.findField(NameMatchTransactionAttributeSource.class, "nameMap"); 47 nameMapField.setAccessible(true); //设置该字段可访问 48 //获取nameMap的值 49 Map<String, TransactionAttribute> map = (Map<String, TransactionAttribute>) nameMapField.get(matchTransactionAttributeSource); 50 51 //遍历nameMap 52 for (Map.Entry<String, TransactionAttribute> entry : map.entrySet()) { 53 if (!entry.getValue().isReadOnly()) {//判断之后定义了ReadOnly的策略才加入到slaveMethodPattern 54 continue; 55 } 56 slaveMethodPattern.add(entry.getKey()); 57 } 58 } 59 60 /** 61 * 在进入Service方法之前执行 62 * @param point 切面对象 003-Spring4 扩展分析BeanPostProcessorBeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor003-spring-data-elasticsearch 3.0.0.0使用-之spring-data简介,CrudRepository,PagingAndSortingRepository等(代码
003-spring cloud gateway-概述基本原理Route Predicate Factory
SPRING IN ACTION 第4版笔记-第六章RENDERING WEB VIEWS-003- SPRING的GENERAL TAG LIBRARY简介及用<s:message>和R