Spring transaction source code-analysis of proxy object generation process

Spring transaction source code

The source code of spring transaction starts with @EnableTransactionManagement annotation

First of all, if we want to use spring transactions, we only need to add @EnableTransactionManagement annotations to the configuration class, and add @Transactional annotations to the business methods (take the spring project as an example, and we will talk about it in the blog behind the SpringBoot project)

@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;
}

As you can see, this annotation is also quite simple, that is, through the @Import annotation, another bean is introduced. By viewing the class inheritance relationship of TransactionManagementConfigurationSelector, you can find that this class implements the ImportSelector annotation, so the selectImports() method will be implemented. In this method, two important beans are injected

  1. AutoProxyRegistrar
  2. ProxyTransactionManagementConfiguration

AutoProxyRegistrar

AutoProxyRegistrar implements the ImportBeanDefinitionRegistrar interface, so in the registerBeanDefinitions method, inject a bean

InfrastructureAdvisorAutoProxyCreator
可以发现,这个类是一个后置处理器,继承了InstantiationAwareBeanPostProcessor

所以:这就是@EnableTransactionManagement的第一个作用,注入了一个后置处理器,这个后置处理器就是用来对事务注解进行增强的

Insert picture description here

ProxyTransactionManagementConfiguration

This class has only one annotation @Configuration, so this class is a configuration class. In this class, three @Bean annotations are used to inject the components used in transaction execution into the spring container.

/**
	 * @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;
	}

So: this class is injected into the spring container, three beans are: transaction interceptor, transaction parser, transaction enhancer

Determine whether an agent is needed

We know that the dynamic proxy of AOP judges whether it needs to be enhanced when the eighth post processor is called. If it needs to be enhanced, it will be proxyed through JDK or CGLIB. The transaction of the annotation version is also realized by AOP.

Insert picture description here

This is to determine whether an enhanced call chain is needed when the bean corresponding to the method of adding transaction annotations is initialized and the eighth post processor is called. Since this is the same call chain as AOP, the middle of these The process will not be explained too much, just look at the final match whether the code needs to be enhanced

We can temporarily think: methodMatcher.matches(method, targetClass) If true is returned here, it needs to be enhanced. If false is returned, then continue to traverse the next method for judgment

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;
		}
	}

The following method is mainly to determine whether the current method is publicly modified, and then call findTransactionAttribute in the method to determine whether the method or class has an @Transactional annotation

	@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;
		}
	}

Here is to parse the configuration information corresponding to the @Transactional annotation, and store the configuration information in a TransactionAttribute object

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;
	}

Up to this point, the parsing is basically complete; if the @Transactional annotation is configured, then the methodMatcher.matches(method, targetClass) method at the top will return true and return true, then the spring post-processor will task the current class There is a transaction method, which needs to be enhanced, then proxy objects will be generated, and the logic of aop is the same:
according to whether the interface is implemented, it is judged whether to use CGLIB proxy or JDK proxy; therefore: @Transactional annotation is similar to aop The point of cut in (only the method with @Transactional annotation will generate proxy object, and when it is called, transaction interceptor will process it)

spring transaction application

1. The reason why spring transaction does not support non-public methods

If you add a transaction annotation to a non-public method, the transaction will not be rolled back when an exception occurs, that is: the transaction is not effective

the reason:

1.我们知道,spring事务其实就是利用了AOP动态代理的知识,也就是说:如果加了@Transactional注解的方法,spring会为其类生成代理对象,在调用的时候,会通过拦截器来调用
2.如果说spring不支持非public方法,那实现原理也简单:在判断是否需要进行动态代理的时候,首先判断下当前class对应的method是否是public的,如果是非public,就不进行后面的判断,直接返回false,无需代理即可;这样的话,就不会为类生成代理对象


The following analytical methods in the source code has spoke of, is the bean in the initialization process, the implementation of the eighth post-processor, when called, to determine whether the bean proxy; In this problem, it is I won't explain it in detail

​ We only need to know: if null is returned here, the class and the method will not be enhanced, if the TransactionAttribute object is returned, it will be enhanced

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;
	}

To solve this problem, you only need to pay attention to this line of code.

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方法才能进行事务代理

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/108694947