Preface
If this is the second time you see the teacher, you are coveting my beauty!
Like + pay attention again, form habit
It doesn't mean anything else. It just needs your camera screen^_^
Special car introduction**
This special train is for Spring Boot transaction source code analysis
Special vehicle problem
- Why can transaction be implemented with @ Transactional annotation?
- What can we learn after analyzing the transaction source code?
Special vehicle terms
affair
In order to avoid dirty data, transactions are usually used in programs to achieve data consistency
Programming services
Start the transaction at the beginning of the business method, and then try catch our business. If there is no exception, commit the transaction. If there is an exception, roll back the transaction in the catch module
Declarative transaction origin
If a programmatic transaction is adopted, the transaction should be opened, try catch, commit or rollback wherever the transaction is needed, which will lead to repeated coding and writing business independent code. Based on the idea of Spring Aop, we can use Aop to enhance the methods that need to use transactions, extract the common parts, and then realize declarative transactions.
Declarative transactions provided by Spring
Add @ Transactional annotation to the business methods that need to use transactions, then you can use the characteristics of transactions, either succeed or fail
Core concepts of Spring Aop
- Cut: cut consists of cut point and notice
- Tangent point: used to match a qualified class or method
- Notification: actions to be performed
Special vehicle analysis
Based on the principle of Spring Boot auto configuration, we should look for the xxxAutoConfiguration class. If we want to look for something related to transactions, it is naturally TransactionAutoConfiguration
Automatic configuration
Open TransactionAutoConfiguration auto configuration class
@Configuration @ConditionalOnBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) public static class EnableTransactionManagementConfiguration { @Configuration @EnableTransactionManagement(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) public static class JdkDynamicAutoProxyConfiguration { } @Configuration @EnableTransactionManagement(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) public static class CglibAutoProxyConfiguration { } }
You can see the annotation @ EnableTransactionManagement for opening transaction manager
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement {}
View classes imported by TransactionManagementConfigurationSelector
protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } }
You can see that autoproxyregister and ProxyTransactionManagementConfiguration are imported
Let's first look at autoproxyregister, which implements importbeandefinitionregister
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); if (candidate == null) { continue; } Object mode = candidate.get("mode"); Object proxyTargetClass = candidate.get("proxyTargetClass"); if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; if (mode == AdviceMode.PROXY) { // Register auto agent Creator AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } }
Register auto agent creator, aopconfigutils - register autoproxycreatorifnecessary
public static BeanDefinition registerAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { // Registered infrastructure advisor autoproxycreator to IOC container return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source); }
Infrastructure advisor autoproxycreator is a subclass of AbstractAutoProxyCreator. AbstractAutoProxyCreator also implements the BeanPostProcessor interface. After bean initialization, the postprocessafterinstance() method will be called. Postprocessafterinstance() is defined in the AbstractAutoProxyCreator class
Post processing of BeanPostProcessor
Open AbstractAutoProxyCreator
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // Wrap bean s if conditions are met return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
This method calls the wrapIfNecessary() method
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // Create proxy if we have advice. // Get aspects and notifications of bean s Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // Need agency if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // Create agent Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
According to the comment, if there is an advice, create the agent,
Finding section
Enter abstractadvisorautoproxycreator - getadvicesandadvisorsforbean
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // Find qualified facets List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); // No eligible facets, no proxy is generated if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
The first sentence of the code is the most important. If there is no qualified aspect, the final result returns null. According to the above analysis, if it is null, the agent will not be created, otherwise, the agent will be created. Let's take a look at the implementation of the first sentence
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // Get all candidate facets, that is, those of type Advisor. The candidate facets obtained here are BeanFactoryTransactionAttributeSourceAdvisor List<Advisor> candidateAdvisors = findCandidateAdvisors(); // Get from the candidate facets to parse the current bean's facets. The final qualified facets are BeanFactoryTransactionAttributeSourceAdvisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
Why is the section obtained above BeanFactoryTransactionAttributeSourceAdvisor? Do you remember that there was a configuration class that was not analyzed when importing it? That's proxytransaction management configuration
Open ProxyTransactionManagementConfiguration
@Configuration public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { // Create BeanFactoryTransactionAttributeSourceAdvisor @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource()); // Set the notice corresponding to the section, which will be used in later analysis advisor.setAdvice(transactionInterceptor()); if (this.enableTx != null) { advisor.setOrder(this.enableTx.<Integer>getNumber("order")); } return advisor; } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } // Create notification @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; } }
Through the above automatic configuration, we can know why the obtained candidate facet is BeanFactoryTransactionAttributeSourceAdvisor
Next, let's look at how to find the aspect from the candidate aspect that can parse the current bean?
protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { // Find the corresponding facet of the current bean that can be resolved return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } }
Search can resolve the corresponding facet of the current bean, aoutilities ා findadvisors that can apply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // already processed continue; } // Whether the current aspect can parse bean s if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
Can candidate facets resolve bean s
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) { // According to the above analysis, the final candidate aspect is BeanFactoryTransactionAttributeSourceAdvisor // This class implements 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; } }
Can candidate facets resolve bean s
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; } // Gets the matching object of tangent point method, which is used to match whether the method conforms to the 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 instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class<?>> classes = new LinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); for (Class<?> clazz : classes) { // Get all Method objects of the current class through reflection Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : // Whether the matching method conforms to methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
Match method transactionattributesourcepointcut × matches
public boolean matches(Method method, Class<?> targetClass) { if (TransactionalProxy.class.isAssignableFrom(targetClass) || PlatformTransactionManager.class.isAssignableFrom(targetClass) || PersistenceExceptionTranslator.class.isAssignableFrom(targetClass)) { return false; } TransactionAttributeSource tas = getTransactionAttributeSource(); // If the transaction property source object is empty or the transaction property object is not null, return true, which means the matching is successful; otherwise, return false, which means the matching fails return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); }
Get transaction attribute object, abstractfallbacktransactionattributesource × gettransactionattribute
public TransactionAttribute getTransactionAttribute(Method method, @Nullable 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 { return cached; } } else { // Calculate transaction property object 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.isTraceEnabled()) { logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr); } this.attributeCache.put(cacheKey, txAttr); } return txAttr; } }
Calculate transaction property object
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) { // Don't allow no-public methods as required. 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. // First, get the transaction property object according to the Method object TransactionAttribute txAttr = findTransactionAttribute(specificMethod); if (txAttr != null) { return txAttr; } // Second try is the transaction attribute on the target class. // If the transaction property object cannot be obtained according to the Method object, the property object will be obtained according to the 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; }
Get the attribute object annotationtransactionattributesource ා findtransactionattribute
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) { return determineTransactionAttribute(clazz); }
Determine transaction property object
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) { for (TransactionAnnotationParser annotationParser : this.annotationParsers) { TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element); if (attr != null) { return attr; } } return null; }
Resolve the transaction attribute object, spring transactionannotationparser ා parsetransactionannotation
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { // Judge whether the element contains @ Transactional annotation. Through the previous analysis, we can draw the following conclusions: // 1. It is preferred to determine whether the method of the class contains @ Transactional annotation. If so, it will be resolved // 2. If all methods do not contain @ Transactional annotation, then judge whether the current class contains @ Transactional annotation. If so, parse it // 3. If the class or a method of the class contains the @ Transactional annotation, the transaction attribute object is not empty, which means that the current bean can be parsed in the secondary aspect AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( element, Transactional.class, false, false); if (attributes != null) { return parseTransactionAnnotation(attributes); } else { return null; } }
Back to abstractautoproxycreator? Wrapifnecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. // There is a value to return here, proxy, otherwise no proxy Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // Agent required if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // Create agent Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
Create agent
Create a proxy abstractautoproxycreator? Createproxy
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // Create agent factory ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } // Build facet, where the facet is BeanFactoryTransactionAttributeSourceAdvisor Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); // Set section proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(getProxyClassLoader()); }
Get proxy proxy
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
Create aop agent
protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } // this here is actually ProxyFactory return getAopProxyFactory().createAopProxy(this); }
aop proxy factory create aop proxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // Create cglib aop agent return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
Instantiate objenesiscglibaaopproxy object
public ObjenesisCglibAopProxy(AdvisedSupport config) { super(config); }
Parent class instantiation
public CglibAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } // The config here is the previous ProxyFactory this.advised = config; this.advisedDispatcher = new AdvisedDispatcher(this.advised); }
Go back to where you got the agent
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
From the above analysis, we can see that createAopProxy() returns CglibAopProxy
Get the proxy through cgliaopproxy, cgliaopproxy × getproxy
public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer... // Create Enhancer object Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } // Setting parent class enhancer.setSuperclass(proxySuperClass); // Setting up interfaces enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); // Get callback, focus on analysis Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); // Set callback type enhancer.setCallbackTypes(types); // Generate the proxy class and create a proxy instance. // Build agent and create agent instance return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
Get callback
private Callback[] getCallbacks(Class<?> rootClass) throws Exception { // Parameters used for optimization choices... boolean exposeProxy = this.advised.isExposeProxy(); boolean isFrozen = this.advised.isFrozen(); boolean isStatic = this.advised.getTargetSource().isStatic(); // Choose an "aop" interceptor (used for AOP calls). // Instantiate the callback, which is executed when the target object method is called Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); return callbacks; }
Instantiate callback part
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { private final AdvisedSupport advised; public DynamicAdvisedInterceptor(AdvisedSupport advised) { // Set facet information, i.e. ProxyFactory this.advised = advised; } @Override @Nullable // Execute when calling the target method public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool... target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // Get notification. The notification here is TransactionInterceptor List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } @Override public boolean equals(Object other) { return (this == other || (other instanceof DynamicAdvisedInterceptor && this.advised.equals(((DynamicAdvisedInterceptor) other).advised))); } /** * CGLIB uses this to drive proxy creation. */ @Override public int hashCode() { return this.advised.hashCode(); } }
Call the processing method of invocation, reflectivemethodinvocation ×
public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // Notification TransactionInterceptor here Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. // Call transactioninterceptor "invoke" return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
Call transactioninterceptor "invoke"
public Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... // Call as a transaction return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
Transaction mode call
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. TransactionAttributeSource tas = getTransactionAttributeSource(); final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); final PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. // Create transaction information object TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. // Call the proxy object method retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception // Business method execution exception, transaction rollback completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { // Clear transaction information object cleanupTransactionInfo(txInfo); } // Submission of affairs commitTransactionAfterReturning(txInfo); return retVal; } else { final ThrowableHolder throwableHolder = new ThrowableHolder(); // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); try { return invocation.proceedWithInvocation(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; return null; } } finally { cleanupTransactionInfo(txInfo); } }); // Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } }
The source code analysis of this transaction is over
Special vehicle summary
- Import autoproxyregister, ProxyTransactionManagementConfiguration configuration class
- Autoproxyregister is used to register infrastructure advisor autoproxycreator to IOC. Infrastructure advisor autoproxycreator implements BeanPostProcessor
- Perform postprocessing of BeanPostProcessor
- Gets the facet created by the ProxyTransactionManagementConfiguration configuration class
- If the bean needs to create a proxy, it needs to create a proxy
- Execute the callback of the agent and get the notification in the callback
- The logic in the notification is: start the transaction, execute the target method, commit or rollback the transaction
Special vehicle review
Review the first two questions:
- Why can transaction be implemented with @ Transactional annotation?
- What can we learn after analyzing the transaction source code?
Through the above analysis, the first problem should be solved, so what functions can we achieve through the knowledge learned above? In the next chapter, we will carry out the actual combat on this basis, and realize the system log function through @ SystemLog annotation. Thank you for taking this special train. Welcome to take it next time
Last
Master, master [Java advanced architect], has gained 15W + programmer's attention in each major platform in just one year, focusing on sharing 20 advanced architecture topics such as Java advanced, architecture technology, high concurrency, microservice, BAT interview, redis topic, JVM tuning, Springboot source code, mysql optimization, paying attention to [Java advanced architect] reply [Architecture], and getting a complete set of 2019 architect video.
Reprint: Please note the source (this article starts with the public address: [java advanced architect].