从上文可以看出,真正把@Transaction注解变成一个TransactionAttribute类的是TransactionAnnotationParser,下面有必要来具体看看它
TransactionAnnotationParser
顾名思义,它是解析方法/类上事务注解的。
// @since 2.5 public interface TransactionAnnotationParser { @Nullable TransactionAttribute parseTransactionAnnotation(AnnotatedElement element); }
它支持上面说的到三个注解,分别对应三个实现类:JtaTransactionAnnotationParser、Ejb3TransactionAnnotationParser、SpringTransactionAnnotationParser。
因为现在基本是Spring的天下了,因此本文只讲述SpringTransactionAnnotationParser,其它的雷同
SpringTransactionAnnotationParser
它专门用于解析Class或者Method上的org.springframework.transaction.annotation.Transactional注解的。
// @since 2.5 此类的实现相对来说还是比较简单的 public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable { // 此方法对外暴露,表示获取该方法/类上面的TransactionAttribute @Override @Nullable public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false); if (attributes != null) { // 此处注意,把这个注解的属性交给它,最终转换为事务的属性类~~~~ return parseTransactionAnnotation(attributes); } // 注解都木有,那就返回null else { return null; } } // 顺便提供的一个重载方法,可以让你直接传入一个注解 public TransactionAttribute parseTransactionAnnotation(Transactional ann) { return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false)); } // 这个简单的说:就是把注解的属性们 专门为事务属性们~~~~ protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { // 此处用的 RuleBasedTransactionAttribute 因为它可议指定不需要回滚的类~~~~ RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); // 事务的传播属性枚举:内部定义了7种事务传播行为~~~~~ Propagation propagation = attributes.getEnum("propagation"); rbta.setPropagationBehavior(propagation.value()); // 事务的隔离级别枚举。一共是4中,枚举里提供一个默认值: 也就是上面我们说的TransactionDefinition.ISOLATION_DEFAULT // 至于默认值是哪种隔离界别:这个具体的数据库有关~~~ Isolation isolation = attributes.getEnum("isolation"); rbta.setIsolationLevel(isolation.value()); // 设置事务的超时时间 rbta.setTimeout(attributes.getNumber("timeout").intValue()); // 是否是只读事务 rbta.setReadOnly(attributes.getBoolean("readOnly")); // 这个属性,是指定事务管理器PlatformTransactionManager的BeanName的,若不指定,那就按照类型找了 // 若容器中存在多个事务管理器,但又没指定名字 那就报错啦~~~ rbta.setQualifier(attributes.getString("value")); // rollbackFor可以指定需要回滚的异常,可议指定多个 若不指定默认为RuntimeException // 此处使用的RollbackRuleAttribute包装~~~~ 它就是个POJO没有实现其余接口 List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(); for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } // 全类名的方式~~ for (String rbRule : attributes.getStringArray("rollbackForClassName")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } // 指定不需要回滚的异常类型们~~~ // 此处使用的NoRollbackRuleAttribute包装 它是RollbackRuleAttribute的子类 for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } // 最后别忘了set进去 rbta.setRollbackRules(rollbackRules); return rbta; } }
通过这个parser就可议把方法/类上的注解,转换为事务属性,然后缓存起来。
这样方法在调用的时候,直接根据Method就能取到事务属性,从而执行不同的事务策略~~~
SavepointManager
管理事务savepoint的编程式API接口。
JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。Connection接口的setSavepoint和releaseSavepoint方法可以设置和释放保存点。
// @since 1.1 public interface SavepointManager { Object createSavepoint() throws TransactionException; void rollbackToSavepoint(Object savepoint) throws TransactionException; void releaseSavepoint(Object savepoint) throws TransactionException; }
它的主要实现有如下:
TransactionStatus这个分支很重要,后面有着重分析。这里先看看JdbcTransactionObjectSupport这个实现
JdbcTransactionObjectSupport
// @since 1.1 继承自SmartTransactionObject public abstract class JdbcTransactionObjectSupport implements SavepointManager, SmartTransactionObject { // 这是Spring定义的类,持有java.sql.Connection // 所以最支不支持还原点、创建还原点其实都是委托给它来的~ @Nullable private ConnectionHolder connectionHolder; ... }
它也只是个抽象类,SmartTransactionObject
接口相关的方法都没有去实现~~~~但是它的子类DataSourceTransactionObject
有去实现的~
关于还原点的实现,整体上还是比较简单的,就是委托给Connection去做~