spring事务源码
spring事务的源码,要从@EnableTransactionManagement注解拉开序幕
首先,如果我们要使用spring事务,只需要在配置类上添加@EnableTransactionManagement注解,并且在业务方法上添加@Transactional注解即可(以spring项目为例,SpringBoot项目后面博客中再另说)
@EnableTransactionManagement
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({
TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
/*
* proxyTargetClass
* true:
* 无论目标方法是否实现了接口,都使用CGLIB代理
* false:
* 如果目标方法实现了接口,使用JDK动态代理
* 如果目标方法没有实现接口,使用CGLIB代理
*/
boolean proxyTargetClass() default false;
/**
* @return
*
* 事务通知模式(切面织入方式):
* 默认是代理模式
*/
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
可以看到,这个注解也挺简单的,就是通过@Import注解,引入了另外一个bean,通过查看TransactionManagementConfigurationSelector的类继承关系,可以发现,这个类实现了ImportSelector注解,所以,会实现selectImports()方法,在该方法中,注入了两个重要的bean
- AutoProxyRegistrar
- ProxyTransactionManagementConfiguration
AutoProxyRegistrar
AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以,在registerBeanDefinitions方法中,注入一个bean
InfrastructureAdvisorAutoProxyCreator
可以发现,这个类是一个后置处理器,继承了InstantiationAwareBeanPostProcessor
所以:这就是@EnableTransactionManagement的第一个作用,注入了一个后置处理器,这个后置处理器就是用来对事务注解进行增强的
ProxyTransactionManagementConfiguration
该类只有一个注解@Configuration,所以该类是一个配置类,在该类中,通过三个@Bean注解,向spring容器中注入了事务执行时要用到的组件
/**
* @return
* 注入事务增强器
* 这里是创建一个advisor,然后设置切点(TransactionInterceptor)和通知(TransactionAttributeSource)
* 这里的BeanFactoryTransactionAttributeSourceAdvisor类似于aop中的advisor
*/
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
//事务增强器会解析事务注解的参数信息
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
/**
* @return
* 往spring容器中注入事务解析器(解析事务注解上的各个参数)
* 在执行第八个后置处理器的时候,判断是否需要增强的时候,会解析transaction注解
*
* 这里在new AnnotationTransactionAttributeSource()对象的时候,有一个非常关键的点:
* publicMethodsOnly 这里在调用构造函数的时候,默认初始化该值为true;该值的意思是:只允许public方法进行事务代理
*
* 在后面判断是否可以对方法进行增强的时候,会判断该值,以及对应method是否是public,如果是非public修饰的方法,直接return null,不进行代理增强
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
/**
* @return
* 定义事务拦截器,并将事务拦截器注入到spring容器中
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
所以:该类是给spring容器中,注入了三个bean,分别是:事务拦截器、事务解析器、事务增强器
判断是否需要代理
我们知道,AOP的动态代理是在第八个后置处理器调用的时候,判断是否需要增强,如果需要增强,就通过JDK或者CGLIB进行代理,注解版的事务,也是利用了AOP实现的
这是在添加事务注解的方法对应的bean在初始化时,调用到第八个后置处理器的时候,判断是否需要进行增强的调用链,由于这里和AOP是一样的调用链,所以中间的这些过程就不做过多解释了,直接看最后匹配是否需要增强的代码
我们可以暂时先认为:methodMatcher.matches(method, targetClass) 如果这里返回的是true,就是需要进行增强,返回false,就继续遍历下一个方法,进行判断
org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut#matches
// 这里是用获取到的tas来判断method是否有添加注解,如果这里返回false,就表示当前method无需增强,返回true,需要增强
public boolean matches(Method method, Class<?> targetClass) {
TransactionAttributeSource tas = this.getTransactionAttributeSource();
return tas == null || tas.getTransactionAttribute(method, targetClass) != null;
}
org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute
/**
* 这里是在解析method之前,先判断下之前是否已经解析过
*/
@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// First, see if we have a cached value.
/**
* 根据method和targetClass生成一个cacheKey
* 如果这里已经对方法进行了一次解析,就会把解析之后获取到的TransactionAttribute对象和对应的key存入到一个map集合中
* 这样下次再有地方用到这个方法的时候,就无须再次解析,直接从map中获取即可
* 如果这里没有获取到,返回的null,就将value设置为null,写入到map中
* 如果获取到对应的txAttr,就setDescriptor设置下该属性,然后写入到map集合中
*/
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return cached;
}
}
else {
// We need to work it out.
/**
* 如果是第一次进入到这里,一定会走这个方法
* 这里就是判断当前方法是否是public,是否有添加@Transactional注解
*/
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
/**
* 如果当前方法没有添加事务注解,或者不满足生成代理对象的要求,就将value设置为null,存入到这个map集合中
*/
if (txAttr == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
// 这里是满足增强的条件,将txAttr放到map集合中,并返回
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
}
if (logger.isDebugEnabled()) {
logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
下面这个方法主要是判断当前method是否是public修饰的,然后在方法中调用findTransactionAttribute来判断方法或者class是否有添加@Transactional注解
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
/**
* 判断当前方法是否是public修饰的,以及是否只支持public方法进行事务代理
*/
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
/**
* 2.查找当前方法是否有添加@Transactional注解,如果有添加,就解析对应的注解信息
* 下面有几个重复查找的动作,这里还没有搞明白依次获取到的是什么,总之都是判断入参的这个方法或者class有没有事务注解(大致的意思应该是先判断方法有没有添加注解,然后再判断类上是否添加事务注解)
*/
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#determineTransactionAttribute
org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
// 这个方法中调用了findMergedAnnotationAttributes来判断当前element是否有事务注解,
// 然后调用parseTransactionAnnotation,解析@Transactional注解的配置
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
这里是解析@Transactional注解对应的配置信息,并将配置信息存入到了一个TransactionAttribute对象中
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
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));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
截止到这里,基本上就解析完毕了;如果有配置@Transactional注解,那么在最上面的methodMatcher.matches(method, targetClass) 方法就会返回true,返回true,那么spring后置处理器就任务当前类中有事务方法,需要进行增强,那就会生成代理对象,和aop的逻辑是一样的:
根据是否实现了接口,来判断是使用CGLIB代理还是使用JDK代理;所以:@Transactional注解就类似于aop中的切点(只有添加了@Transactional注解的方法,才会生成代理对象,在被调用的时候,进行事务拦截器进行处理)
spring事务应用
1.spring事务不支持非public方法的原因
如果在非public方法上添加事务注解,在发生异常的时候,事务是不会回滚的,也即:事务是不生效的
原因:
1.我们知道,spring事务其实就是利用了AOP动态代理的知识,也就是说:如果加了@Transactional注解的方法,spring会为其类生成代理对象,在调用的时候,会通过拦截器来调用
2.如果说spring不支持非public方法,那实现原理也简单:在判断是否需要进行动态代理的时候,首先判断下当前class对应的method是否是public的,如果是非public,就不进行后面的判断,直接返回false,无需代理即可;这样的话,就不会为类生成代理对象
下面这个方法在源码解析中有说到过,是bean在初始化的过程中,执行到第八个后置处理器的时候,调用的,判断是否需要对bean进行代理;在这个问题中,就不详细解释了
我们只需要知道:如果这里返回的是null,就不会对该类和该方法进行增强,如果返回了TransactionAttribute对象,就会进行增强
org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
/**
* 判断当前方法是否是public修饰的,以及是否只支持public方法进行事务代理
*/
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
/**
* 2.查找当前方法是否有添加@Transactional注解,如果有添加,就解析对应的注解信息
*/
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
针对这个问题,只需要关注这一行代码就可以
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
/**
* 这里就是判断当前方法是否是public方法修饰的
*/
public static boolean isPublic(int mod) {
return (mod & PUBLIC) != 0;
}
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#allowPublicMethodsOnly
@Override
protected boolean allowPublicMethodsOnly() {
return this.publicMethodsOnly;
}
在AnnotationTransactionAttributeSource这个类中,搜索可以发现,默认的都是true,并且这个类的初始化是在
ProxyTransactionManagementConfiguration中,在这个ProxyTransactionManagementConfiguration中,通过@Bean注解,注入了一个TransactionAttributeSource对象,
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
所以:这个参数铁定是true;也就是说,只允许public方法才能进行事务代理