Review AOP related knowledge points:

  1. Static proxy versus JDK dynamic proxy versus CGLIB dynamic proxy
  2. The difference between InstantiationAwareBeanPostProcessor and BeanPostProcessor in Spring
  3. Spring Source Series 8: Creation of proxies for AOP source code parsing

We summarize the AOP formula

  • JDK dynamic Proxy+InvocationHandler +advised
  • CGLB dynamic proxy (Enhancer+MethodInterceptor)+advised

Essentially, new bytecode classes are generated in memory.

In this section we look at how transactions are implemented using AOP.

Transaction broker generation process

A, @ EnableTransactionManagement configuration transaction environment

@ EnableTransactionManagement @ Import (TransactionManagementConfigurationSelector. Class) to introduce TransactionManagementConfigurati OnSelector. Class. This kind of

@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {
						TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null; }}Copy the code

PROXY: in this case, two classes are registered:

> AutoProxyRegistrar:

ImportBeanDefinitionRegistrar is realized. Its interface method, registerBeanDefinitions, registers the Bean definitions with the repository

InfrastructureAdvisorAutoProxyCreator. Inherited AbstractAutoProxyCreator InfrastructureAdvisorAutoProxyCreator inherited AbstractAdvisorAutoProxyCreator indirectly

We covered this in the AOP proxy generation section. Inherited AbstractAutoProxyCreator AnnotationAwareAspectJAutoProxyCreator also indirectly.

Abstracta To ProxyCreator does most of the work in AOP implementation principle.

In this sense, the transaction proxy object creation process is the same as the AOP proxy object creation process, with the abstractautoXyCreator class as the key

>ProxyTransactionManagementConfiguration

It’s an @Configuration. There are three @bean annotation methods.

  • transactionAdvisor()
  • transactionAttributeSource()
  • transactionInterceptor()
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(a) {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		advisor.setAdvice(transactionInterceptor());
		advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource(a) {
		return new AnnotationTransactionAttributeSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(a) {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager ! =null) {
			interceptor.setTransactionManager(this.txManager);
		}
		returninterceptor; }}Copy the code

1. BeanFactoryTransactionAttributeSourceAdvisor:

First: transactionAdvisor () method to register a BeanFactoryTransactionAttributeSourceAdvisor in warehouse.

From the perspective of its inheritance, it is an Advisor and a PointcutAdvisor. In the previous section, we analyzed that advisors are wrapped (Advice+Pointcut).

Now that we have advisors, where are Advice and Pointcut?

BeanFactoryTransactionAttributeSourceAdvisor a pointcut attributes, a TransactionAttributeSourcePointcut will be new.

private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		protected TransactionAttributeSource getTransactionAttributeSource(a) {
			returntransactionAttributeSource; }};Copy the code

From its inheritance, we can see that it is actually a Pointcut.

At this point, Advice is left undiscovered.

2. TransactionAttributeSource: second: TransactionAttributeSource () to register a AnnotationTransactionAttributeSource in warehouse. What this AnnotationTransactionAttributeSource for?

BeanFactoryTransactionAttributeSourceAdvisor. SetTransactionAttributeSource (transactionAttributeSource ()) attribute valuesprivate TransactionAttributeSource transactionAttributeSource;
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		protected TransactionAttributeSource getTransactionAttributeSource(a) {
			returntransactionAttributeSource; }}; TransactionAttributeSourcePointcut matche method@Override
	public boolean matches(Method method, Class
        targetClass) {
		if(targetClass ! =null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
			return false;
		}
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null|| tas.getTransactionAttribute(method, targetClass) ! =null);
	}
Copy the code

AnnotationTransactionAttributeSource object is assigned to BeanFactoryTransactionAttributeSourceAdvisor transactionAttributeSource attributes. Pointcut attribute initialization time, new a TransactionAttributeSourcePointcut class and implement getTransactionAttributeSource () method, Just return the transactionAttributeSource attribute getTransactionAttributeSource () method.

That is to say TransactionAttributeSourcePointcut getTransactionAttributeSource () method returns the AnnotationTransactionAttributeSource

Finally, the TransactionInterceptor () method, which registers a TransactionInterceptor class in the repository.

The TransactionInterceptor is an Advice. The enhancer is where the transaction is actually processed.

With the Advice + Pointcut. Advice + Pointcut = Advisor. Advisor+TargetSource = Advised

With an Advised approach, spring transactions just apply AOP basics.

Second, proxy generation

In AOP source code analysis that section, as we said before, rear postProcessAfterInitialization initialization method, wrapIfNecessary satisfy conditions, to create the agent.

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if(bean ! =null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

But this condition is: getAdvicesAndAdvisorsForBean (bean getClass (), beanName, null); The ability to get the Advisor that applies to the current bean

1. The Advisor is looking for

Let’s recall the previous section:

GetAdvicesAndAdvisorsForBean after AbstractAdvisorAutoProxyCreator. FindEligibleAdvisors calls, AopUtils. FindAdvisorsThatCanApply (candidateAdvisors beanClass) call, will call AopUtils. CanApply to judge whether an Advisor is applicable to the current class.

Let’s look at the canApply method

public static boolean canApply(Advisor advisor, Class<? > targetClass,boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
		else if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pca = (PointcutAdvisor) advisor;
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// It doesn't have a pointcut so we assume it applies.
			return true; }}Copy the code

As mentioned above,

  • BeanFactoryTransactionAttributeSourceAdvisor is a PointcutAdvisor type of Advisor
  • New a TransactionAttributeSourcePointcut BeanFactoryTransactionAttributeSourceAdvisor.

So go: canApply(pca.getPointcut(), targetClass, hasIntroductions).

Pca. GetPointcut TransactionAttributeSourcePointcut () returns

CanApply (Pointcut PC, Class<? > targetClass, boolean hasIntroductions)

public static boolean canApply(Pointcut pc, Class<? > targetClass,boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		if(! pc.getClassFilter().matches(targetClass)) {return false;
		}

		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceofIntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class<? >> classes =newLinkedHashSet<Class<? >>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass);for(Class<? > clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {
				if((introductionAwareMethodMatcher ! =null &&
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
						methodMatcher.matches(method, targetClass)) {
					return true; }}}return false;
	}
Copy the code
  • Check classfilter. matches.TransactionAttributeSourcePointcutThe parent classStaticMethodMatcherPointcut.classFilter= ClassFilter.TRUEIndicates that all class checks pass
  • Matches: matches
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
	@Override
	public boolean matches(Method method, Class
        targetClass) {
		if(targetClass ! =null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
			return false;
		}
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null|| tas.getTransactionAttribute(method, targetClass) ! =null); }}Copy the code

Matches method will be called getTransactionAttributeSource () to obtain a TransactionAttributeSource object, Through TransactionAttributeSource. GetTransactionAttribute (method, targetClass), to judge the adaptability.

About getTransactionAttributeSource () mentioned above. Returns a AnnotationTransactionAttributeSource instance objects.

In other words: TransactionAttributeSourcePointcut matches () method is through AnnotationTransactionAttributeSource getTransactionAttribute (method, TargetClass).

Take a look at the getTransactionAttribute() method

	@Override
	public TransactionAttribute getTransactionAttribute(Method method, Class
        targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			return null;
		}

		// First, see if we have a cached value.
		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 {
				returncached; }}else {
			// We need to work it out.
			TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
			// Put it in the cache.
			if (txAttr == null) {
				this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
			}
			else {
				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);
			}
			returntxAttr; }}Copy the code

Here in a cache, but the key in the TransactionAttribute txAttr = computeTransactionAttribute (method, targetClass);

protected TransactionAttribute computeTransactionAttribute(Method method, Class
        targetClass) {
		// Don't allow no-public methods as required.
		
		// Non-public method transactions do not take effect.
		if(allowPublicMethodsOnly() && ! Modifier.isPublic(method.getModifiers())) {return null;
		}

		// Ignore CGLIB subclasses - introspect the actual user class.Class<? > userClass = ClassUtils.getUserClass(targetClass);// 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.
		
		// Get the real method (if there is an interface method, get the method on the implementation class)
		Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
		// If we are dealing with method with generic parameters, find the original method.
		specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

		// First try is the method in the target class.
		// First check to see if there is a @Transactional annotation on the implementation class method
		TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
		if(txAttr ! =null) {
			return txAttr;
		}

		// Second try is the transaction attribute on the target class.
		// Next check to see if the class that implements the Transactional method has the @Transactional annotation on it
		txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
		if(txAttr ! =null && ClassUtils.isUserLevelMethod(method)) {
			return txAttr;
		}

		if(specificMethod ! = method) {// Fallback is to look at the original method.
			// See if there is a @transactional annotation on the interface method
			txAttr = findTransactionAttribute(method);
			if(txAttr ! =null) {
				return txAttr;
			}
			// Last fallback is the class of the original method.
			// Finally, see if the interface has the @Transactional annotation
			txAttr = findTransactionAttribute(method.getDeclaringClass());
			if(txAttr ! =null && ClassUtils.isUserLevelMethod(method)) {
				returntxAttr; }}return null;
	}
Copy the code

Therefore, we finally know the Transactional annotation search order, implement class methods implement interface methods implement the Transactional annotation in all four places for the transaction to take effect.

In the four places any one place found @ Transactional annotation, explain BeanFactoryTransactionAttributeSourceAdvisor is suitable for the current class, GetAdvicesAndAdvisorsForBean returned not empty. Now you can create the dynamic proxy.

2. Proxy generation
// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
Copy the code

The createProxy() method, which we examined in the Spring source Series 8: Creating proxies for AOP source code parsing section.

The end result is either cglibaopproxy.getProxy () returning the proxy object or jdkDynamicaopproxy.getProxy () returning the proxy object

Conclusion:

Spring transactions are implemented based on Spring AOP. Transactions are implemented by defining a perfectly AOP friendly Advisor (Advice+Pointcut) for transactions.

Let’s look at this formula:

  1. AOP = dynamic proxy + Advised(Adisor+TargetSource);
  2. Adisor = Advice+Pointcut

What if we could use this formula to create something like a transaction?