This is the 23rd day of my participation in the August More Text Challenge.More challenges in August

AOP section source code analysis

Source code analysis is divided into three parts

1. Parse the cut

2. Create a dynamic proxy

3. Call


 

  • Source code entry
Entry to source code analysis, starting with annotations: The entry to a component is an annotation, such as the AOP-enabled annotation @enableAspectJAutoProxy. In the annotation implementation class, there will be an @import (""). This @import ("") is the imported source implementation class. Such as AOP @ Import (AspectJAutoProxyRegistrar. Class)Copy the code
Spring usually adds a comment to enable a feature. If you want to see the source code for a feature, you can follow the comment to the @import ("") source code entryCopy the code

Entry to source code analysis, AOP annotation:

package com.lxl.www.aop; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; @enableAspectJAutoProxy @ComponentScan("com.lxl.www.aop") public class MainConfig {}Copy the code

To introduce AOP, we need to add the @EnableAspectJAutoProxy proxy to the configuration file. So to remove the introduction of AOP, just comment out this annotation. This annotation explains the entire AOP entry.

Tip: The introduction of other components is similar, usually importing components requires an annotation, and the entry to the whole functionality is on the host.

Next, go to the annotation class

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
 @Import(AspectJAutoProxyRegistrar. class) public @interface EnableAspectJAutoProxy {

    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;

}
Copy the code

At this point, we see that the EnableAspectJAutoProxy class adds an @import annotation class, and we know that Import annotations can add a bean to the IoC container.

 

Below into the AspectJAutoProxyRegistrar class

 

package org.springframework.context.annotation; import org.springframework.aop.config.AopConfigUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { /** * Register, escalate, and configure the AspectJ auto proxy creator based on the value * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing * {@code @Configuration} class. */ @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AopConfigUtils. RegisterAspectJAnnotationAutoProxyCreatorIfNecessary (registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy ! = null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}}Copy the code

We see that to use ImportBeanDefinitionRegistrar registered a BeanDefinition.

Need to keep in mind that usually used ImportBeanDefinitionRegistrar @ Import can register a BeanDefinition into the container.

How do you register? Look at the implementation.

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
Copy the code

Registered name is internalAutoProxyCreator AnnotationAwareAspectJAutoProxyCreator

@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @ Nullable Object source) {/ * * * register a AnnotationAwareAspectJAutoProxyCreator type of bean definitions * / return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }Copy the code

 

The above structure is combed as follows:

 

We see that registered the class AnnotationAwareAspectJAutoProxyCreator types of beans. What kind of class is this? Let’s look at the structure of the class. This class has a very large inheritance structure, so let’s just look at the inheritance structure that is relevant for this article

Parsing the facets and creating the dynamic proxy are all done in the bean’s post-processor. See the AOP implementation principle and createBean process below

 

The figure above shows the nine post-processors invoked during bean loading. Before creating bean invokes the rear InstantiationAwareBeanPostProcessor processor determine whether need to create an AOP for this class, also is the process of analytical aspects. So in AnnotationAwareAspectJAutoProxyCreator implements the rear InstantiationAwareBeanPostProcessor processor interface. Rewrite the postProcessBeforeInstantiation method.

After the third stage of createBean initialization, to create the AOP’s dynamic proxy, the BeanPostProcess post-processor is called, AnnotationAwareAspectJAutoProxyCreator also implements BeanPostProcess interface. Rewrite the postProcessAfterInitialization.

At the same time also need to deal with the problem of AOP circular dependencies, processing cycle is dependent on the attribute assignment before calling SmartInstantiationAwareBeanPostProcessor post processor, then rewrite getEarlyBeanReference method. We see AnnotationAwareAspectJAutoProxyCreator also implements SmartInstantiationAwareBeanPostProcessor interface. Rewrite the getEarlyBeanReference method.

 

1) AOP parsing aspect

 

Through the above analysis, we know, analytical aspects is the rewrite the rear InstantiationAwareBeanPostProcessor processor postProcessBeforeInstantiation method. So, we need to find AnnotationAwareAspectJAutoProxyCreator rewrite postProcessBeforeInstantiation method.

How do you find tips? Use the shortcut key CTRL + O in IDEA to find all the methods overridden by the current class. In the search postProcessBeforeInstantiation, should be able to find itCopy the code

The post-processor into the bean that created the dynamic proxy is the first entry to the parsing aspect

@Override public Object postProcessBeforeInstantiation(Class<? > beanClass, String beanName) { ...... }Copy the code

We play a breakpoint at the entrance of the postProcessBeforeInstantiation method, then take a look at this interface invocation chain

 

As shown above, we can see that our entrance is the main method, and then call the refresh () method, execute the refresh () method of finishBeanFactoryInitialization () method, We call createBean() under doGetBean(). Then call is resolveBeforeInstantiation applyBeanPostProcessorsBeforeInstantiation method, access to all the bean here post processor, To determine whether the rear of the bean processors InstantiationAwareBeanPostProcessor an instance. If it is, then call postProcessBeforeInstantiation () method.

@Nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class<? > beanClass, String beanName) {/** * get all the post-handlers in the container * there was a method defined by the registration bean before this, which was already registered. So here you can get the list * * the post processor of nine bean, is a class implements InstantiationAwareBeanPostProcessor class, * / rewrite postProcessBeforeInstantiation method for (BeanPostProcessor bp: getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result ! = null) { return result; } } } return null; }Copy the code

 

Here is to analyze postProcessBeforeInstantiation () method

@Override public Object postProcessBeforeInstantiation(Class<? > beanClass, String beanName) {/** * when the first bean is created, all bean afterprocessors are called and all facets are parsed. CacheKey = getCacheKey(beanClass, beanName); // No beanName or not included in targetSourcedBeans if (! StringUtils.hasLength(beanName) || ! This. TargetSourcedBeans. The contains (beanName)) {/ / determine whether has been parsed? If (this. AdvisedBeans. Either containsKey (cacheKey)) {/ / parsed, the direct return to return null; } /* * Determine if the current class needs to be skipped. If it is a base class or a class that should be skipped, null is returned, indicating that the class does not need to be parsed * * to determine whether it is a base bean(whether it is a facet class, notification, pointcut). Because if the class itself is a notification, slice, then we don't need to parse it * skipping classes: the default is false. In shouldSkip, get all the bean definitions, is the @Aspect tag, Then each notification generated an advisor * / if (isInfrastructureClass (beanClass) | | shouldSkip (beanClass, BeanName)) {/** * advisedBean is a collection that holds whether the class is a advise */ this.AdvisedBeans.put (cacheKey, Boilo.false); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target  bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource ! = null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); // createProxy Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; }Copy the code

Step 1: Build the cache

CacheKey = getCacheKey(beanClass, beanName);Copy the code
When the first bean is created, the backend processor for all beans is called and all facets are parsed. This step is very performance intensive. So, it's going to be in cache. Those that have been created will not be created laterCopy the code

Step 2: Verify that the bean has been parsed. If it has been parsed, it is no longer parsed

/ / determine whether has been parsed the if (this. AdvisedBeans. Either containsKey (cacheKey)) {/ / parsed, the direct return to return null; }Copy the code

Step 3: Determine if the class needs to be skipped

If (isInfrastructureClass (beanClass) | | shouldSkip (beanClass, beanName)) {/ * * * advisedBean is a collection, Advise */ this.AdvisedBeans.put (cacheKey, Boolea.false); return null; }Copy the code

If it is a base class or a class that should be skipped, null is returned, indicating that the class does not need to be resolved.

There are two judgments here.

IsInfrastructureClass (beanClass) determines whether the current class is a base class, which means Advice, Pointcut, Advisor, AopInfrastructureBean. If the class itself is the base class, then do not need to parse

protected boolean isInfrastructureClass(Class<? > beanClass) {// If the class is an Advice class, or a Pointcut class, or a Adivsor class, Or AOPInsfrastructureBean type classes. Boolean retVal = Advice. Class. IsAssignableFrom (beanClass) | | Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); if (retVal && logger.isTraceEnabled()) { logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]"); } return retVal; }Copy the code

ShouldSkip (beanClass, beanName) checks if the current class needs to be skipped.

@Override protected boolean shouldSkip(Class<? > beanClass, String beanName) {// Find the candidate Advisors( List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName); }Copy the code

 

findCandidateAdvisors(); Find the candidate class, and then construct the candidate class as an Advisor object. Go into the method and see how candidates are screened out.

AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()

@Override protected List<Advisor> findCandidateAdvisors() { // Add all the Spring advisors found according to superclass Rules. / / find the way XML configuration Advisor and native interface AOP Advisor and find related affairs Advisor List < Advisor > advisors. = super findCandidateAdvisors (); // Build Advisors for all AspectJ aspects in the bean factory. Encapsulation is an Advisor if (this. AspectJAdvisorsBuilder! = null) {//buildAspectJAdvisors() is used to parse the Aspect class to see if it contains @aspect annotations, Then each notification generated an advisor advisors. The addAll (this) aspectJAdvisorsBuilder) buildAspectJAdvisors ()); } // Return all notifications return advisors; }Copy the code

There are two things going on here

Step 1: Parse xmL-configured Advisors (including native interface configured advisors and find transaction-related advisors)

Step 2: Parse the aspect of the annotation approach. The buildAspectJAdvisors() method is used to parse the aspect class. The notification methods in each aspect class are parsed and a pointcut expression is matched for each method.

 

public List<Advisor> buildAspectJAdvisors() { /* * aspectNames: List<String> aspectNames = this.aspectBeannames; aspectNames = this.aspectBeannames; / / if aspectNames value is null, then the first singleton beans rear called when the processor execution (AnnotationAwareAspectJAutoProxy) if (aspectNames = = null) {/ / lock, Prevent multiple threads from loading Aspect synchronized (this) {aspectNames = this.aspectBeannames; // Double check if (aspectNames == null) {// Save all notifications parsed from the section List<Advisor> advisors = new ArrayList<>(); AspectNames = new ArrayList<>(); /* * Scan subclasses of Object. The Object to be scanned is Object.class. That is, scan all the classes in the container. This process is very performance intensive, so Spring has added caching to save aspects * * except for transactions, Spring does not cache the transaction module */ String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); // loop over beanNames for (String beanName: beanNames) {if (! isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this case they // would be cached by the Spring container But would not have been weaved. // Get the corresponding class <? > beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; If (this.advisorFactory.isAspect (beanType)) {aspectNames.add(beanName); Amd = new AspectMetadata(beanType, beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); / / analytical aspects all notice, a notice in the class generated an Advisor. The List < Advisor > classAdvisors = this. AdvisorFactory. GetAdvisors (factory); / / added to the cache if (this. The beanFactory. IsSingleton (beanName)) {this. AdvisorsCache. Put (beanName classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this. if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } List<Advisor> advisors = new ArrayList<>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors ! = null) { advisors.addAll(cachedAdvisors); } else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }Copy the code

So let’s see how do we make a List

/ / analytical aspects all notice, a notice in the class generated an Advisor. The List < Advisor > classAdvisors = this. AdvisorFactory. GetAdvisors (factory);Copy the code
Advisor public List < > getAdvisors (MetadataAwareAspectInstanceFactory aspectInstanceFactory) {/ / get marked @ the Aspect of Class <? > aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); / / get the name of the class the final String aspectName = aspectInstanceFactory. GetAspectMetadata () getAspectName (); // Validate the section class validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. // Wrap the aspectInstanceFactory with a wrapper pattern, Build into MetadataAwareAspectInstanceFactory class MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new ArrayList<>(); // Get all the notification methods in the section class except for the @pointcut annotation for (Method Method: GetAdvisorMethods (aspectClass)) {// Resolve candidate methods to Advisor. Advisor contains advise and pointcut. Note: getAdvisor () method defined in the order of plane analytic Advisor Advisor = getAdvisor (method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor ! = null) { advisors.add(advisor); } } // If it's a per target aspect, emit the dummy instantiating aspect. if (! advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // Find introduction fields. for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor ! = null) { advisors.add(advisor); } } return advisors; }Copy the code

 

There are two main points here. The first is getAdvisorMethods(aspectClass), which gets all advisormethods of the current aspectClass. The second is the encapsulated Advisor object

  • Step 1: parse all the advice methods in the aspectClass.

    @param aspectClass * @return */ private List<Method> getAdvisorMethods(Class<? > aspectClass) { final List<Method> methods = new ArrayList<>(); / / call doWithMethods. The second parameter is an anonymous function, rewrite the ReflectionUtils doWith method. DoWithMethods (aspectClass method - > {/ / analytical aspects of all the methods in In addition to the Pointcut if (AnnotationUtils getAnnotation (method, Pointcut. Class) = = null) {the methods. The add (method); } }, ReflectionUtils.USER_DECLARED_METHODS); If (methods.size() > 1) {// Sort methods.sort(METHOD_COMPARATOR); } return methods; }Copy the code

     

The method is to scan all the methods of the aspect class and add them to methods, except for the methods annotated with Pointcut

And then sort the methods, how do you sort them?

private static final Comparator<Method> METHOD_COMPARATOR; static { Comparator<Method> adviceKindComparator = new ConvertingComparator<>( new InstanceComparator<>( Around. class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (Converter<Method, Annotation>) method -> { AspectJAnnotation<? > ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (ann ! = null ? ann.getAnnotation() : null); }); Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName); METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator); }Copy the code

 

Order notification methods in the order Aroud, Before, After, AferReturning, and AfterThrowing

  • Step 2: Resolve the candidate method to Advisor. There are also two steps here. Details are as follows:

    */ @override @nullable public Advisor getAdvisor(Method) candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); / / to get candidate method in plane tangent point expression AspectJExpressionPointcut expressionPointcut = getPointcut (candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } / / the point of tangency expressions and advised to encapsulate InstantiationModelAwarePointcutAdvisorImpl objects, This is an Advisor to inform return new InstantiationModelAwarePointcutAdvisorImpl (expressionPointcut candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }Copy the code

     

Method is parsed in getPointcut, along with the pointcut expression pointcut

/** * Find out which type of Aspectj notification method the candidate method belongs to * @param candidateAdviceMethod The candidate notification method * @param candidateAspectClass the candidate section class * @return */ @Nullable private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<? > candidateAspectClass) {// First step: parse the annotations on the candidate method, like @before (value="pointcut()") // Find the Aspectj annotations: @Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing AspectJAnnotation<? > aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } // Step 2: Parsing the aspect aspect of the tangent point expression AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut (candidateAspectClass, new String [0], new Class<? > [0]); / / analytic expression point of tangency ajexp. SetExpression (aspectJAnnotation. GetPointcutExpression ()); if (this.beanFactory ! = null) { ajexp.setBeanFactory(this.beanFactory); } return ajexp; }Copy the code

 

As you can see from the code above, there are also two operations. The method is resolved to Advise, and the pointcut expression in the section class is resolved. Returns returns the pointcut expression.

Next, wrap the candidate method and pointcut expression as Advisor. In getAdvisor(…) Method:

/ / the point of tangency expressions and advised to encapsulate InstantiationModelAwarePointcutAdvisorImpl objects, This is an Advisor to inform return new InstantiationModelAwarePointcutAdvisorImpl (expressionPointcut candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);Copy the code

 

(expressionPointcut) CandidateAdviceMethod: the candidate method

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {this.declaredPointcut = declaredPointcut; / / section this. DeclaringClass = aspectJAdviceMethod. GetDeclaringClass (); / / section method name this. MethodName = aspectJAdviceMethod. The getName (); / / section method parameter of type enclosing parameterTypes. = aspectJAdviceMethod getParameterTypes (); This.aspectjadvicemethod = aspectJAdviceMethod; / / aspectJ to inform the factory this. AspectJAdvisorFactory = aspectJAdvisorFactory; / / aspectJ an instance of the factory this. AspectInstanceFactory = aspectInstanceFactory; /** * Advisors are sorted Around, Before, After, AfterReturning, and AfterThrowing. Is the size of the advisors. If size is 0, then it is the first method. */ this. DeclarationOrder = 'Around'; // Section name this.aspectName = aspectName; if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // Static part of the pointcut is a lazy type. Pointcut preInstantiationPointcut = Pointcuts.union( aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); this.pointcut = new PerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy = true; } else { // A singleton aspect. this.pointcut = this.declaredPointcut; this.lazy = false; this.instantiatedAdvice = instantiateAdvice(this .declaredPointcut); }}Copy the code

 

You’ve got the pointcut expression, and Advice is initialized, depending on the type of Advice.

  • Surround the notification, building an object around the notification
  • Pre-notification, build a pre-notification object
  • Post-notification, which builds an object for post-notification
  • Exception notification, which builds an exception notification object
  • Returns notifications, building an object that returns notifications

The specific code is as follows:

@Override @Nullable public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {/ / candidate cut Class <? > candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); // The annotation content on the notification method AspectJAnnotation<? > aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } // If we get here, we know we have an AspectJ method. // Check that it's an AspectJ-annotated class if (! isAspect(candidateAspectClass)) { throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) { case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } return null; case AtAround: // springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtBefore: / / in front to notify the object springAdvice = new AspectJMethodBeforeAdvice (candidateAdviceMethod expressionPointcut, aspectInstanceFactory); break; case AtAfter: SpringAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: / / encapsulated into a return to notify the object springAdvice = new AspectJAfterReturningAdvice (candidateAdviceMethod expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: / / encapsulation exception notification object springAdvice = new AspectJAfterThrowingAdvice (candidateAdviceMethod expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } // Now to configure the advice... springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames ! = null) { springAdvice.setArgumentNamesFromStringArray(argNames); } springAdvice.calculateArgumentBindings(); return springAdvice; }Copy the code

 

This is what we said in the previous structure. When the cut is parsed, each method in the cut is parsed into an Advisor, and each Advisor contains two parts :Advise and pointcut.

Finally, all the aspect classes are parsed and all advisors are returned in the collection advisors.

 

This completes the parsing of the section.

 

2) Invoke the dynamic proxy

When are dynamic proxies created during IOC resolution?

Dynamic proxies are typically created after the creation bean is initialized. If there are circular dependencies, the dynamic proxy is created after instantiation, and then you get a feel for the bean creation process.

 

Let’s look at the normal process of creating an AOP dynamic proxy after initialization.

In the process of creating bean, a total of three steps, to see AbstractAutowireCpableBeanFactory. DoCreateBean ()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, MBD, args); } / / use the decorator design pattern here final Object bean. = instanceWrapper getWrappedInstance (); Class<? > beanType = instanceWrapper.getWrappedClass(); . Try {// Step 2: Fill the property, assign the property (call the set method) here also call the post-processor populateBean(beanName, MBD, instanceWrapper); ExposedObject = initializeBean(beanName, exposedObject, MBD); }... }Copy the code

 

At step 3 initialization, there are a number of bean afterprocessors to deal with.

@Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    } 
Copy the code

 

postProcessAfterInitialization(result, beanName); Is to handle the post-initialization processor, from this method as an entry point analysis.

AnnotationAwareAspectJAutoProxyCreator also implements postProcessAfterInitialization (result, beanName); interface

@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName (String beanName) {/** ** Every bean parses its facets. Because there is another way to implement AOP - by implementing the Advisor interface - new beans can be parsed at any time during the load process. Therefore, it needs to be parsed again every time. * All the advisors that were parsed the first time have been put into the cache. In this case, they will be fetched from the cache first, that is, the parsed advisors will not be parsed again. That is, no performance cost */ if (bean! CacheKey = getCacheKey(bean.getClass(), beanName); /** * Because it is possible to create the state once during the loop dependency processing, if so, it will not be created now, and delete * in this case, we are dealing with the normal class dynamic proxy, so, Need to create dynamic agent since the circulation delete * / if (this. EarlyProxyReferences. Remove (cacheKey)! // This method returns an instance of the dynamic proxy. Return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }Copy the code

 

One important point to emphasize here is that each bean parses the aspect as it is parsed. Why do we have to parse it every time?

Because there are two ways to create a section, one is to implement the Advisor interface and the other is to annotate. The Advisor interface is implemented in such a way that new beans can be parsed at any time during the loading process. So, you need to reparse it every time.

The Advisor that we parsed for the first time has been put into the cache. In this case, it will be fetched from the cache first, that is, the Advisor that has been parsed will not be parsed again. So it doesn’t cost performance

 

The next process is as follows:

 

Here, step 3: Delete the dynamic proxy object created by the loop dependency. Why?

Because it is possible that dynamic proxy beans were already created at the time of the cyclic dependency processing, if so, they are no longer created and removed.

Here, we are dealing with the dynamic proxy of a normal class, so we need to delete the dynamic proxy created by the loop dependency

 

Note: The earlyProxyReferences object is used to store dynamic proxy beans created during loop dependencies. If the loop dependency created the proxy bean, then return it, if not, we create it again.

 

Now let’s see how it’s created. Okay?

Protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { TargetSourcedBeans used to store its own logic to create the dynamic proxy if (StringUtils. HasLength (beanName) && enclosing targetSourcedBeans. The contains (beanName)) { return bean; } // Determine whether the bean needs to be enhanced /** * which classes need not be enhanced? */ if (boil.false. Equals (this.advisedBeans.get(cacheKey))) {return bean; } / / judgment is a base class, or whether it is need to skip classes if (isInfrastructureClass (bean. GetClass () | | shouldSkip (bean. GetClass (), BeanName)) {// Mark it as an unenhanced class this.AdvisedBeans.put (cacheKey, Boolea.false); return bean; } // Create a dynamic proxy by matching the advisors according to the class. Otherwise don't create the dynamic proxy Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean. GetClass (), beanName, null); // Match at least one Advisor and create a dynamic proxy 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; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }Copy the code

 

Take a look at the creation process

 

First determine if it is a class that needs to be skipped. Which classes do you want to skip?

The first category: basic. Advice, Pointcut, Advisor, AopInfrastructureBean.

The second type: the original interface class to. Class beginning with ORIGINAL

 

Next, match the Advisor.

protected List<Advisor> findEligibleAdvisors(Class<? > beanClass, String beanName) {// First step: List<Advisor> candidateAdvisors = findCandidateAdvisors(); // Step 2: check whether the advisor can act on the current bean. List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // Step 3: Enhance extendAdvisors(eligibleAdvisors) to the advisor of the matching bean; // Step 4: Sort the advisors of matching beans if (! eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } // advisors return eligibleAdvisors; }Copy the code

 

There are four steps, as detailed in the code and comments above.

  • Step 1: Get the parsed advisors from the cache
  • Step 2: Loop to determine if the Advisor can act on the current bean
  • Step 3: Enhance the advisor for the matching bean
  • Step 4: Sort the advisors for matching beans

 

The first step here is to fetch the resolved advisors collection from the cache. The resolution is to fetch the parsed advisors from the cache

Next, loop through the advisors obtained to obtain each advisor. Determine if the Advisor is a notification that the target bean needs to be enhanced.

 

Here, during the filtering, two filters are made based on the pointcut expression. The first coarse screening, the second is fine screening. Return true for all target classes that match the pointcut expression.

The next step is to create the dynamic proxy. The created dynamic proxy object is then returned.

Let’s look at how to create a dynamic proxy.

  

There are two ways to create dynamic proxy objects: JDK proxies and Cglib proxies.

In both XML configuration and annotation mode, there is a proxy-target-class parameter. If this parameter is set to true, the cglib proxy is forced to be used. The Settings are as follows:

@enableAspectJAutoProxy (proxyTargetClass =true) < AOP: sapectj-autoproxy proxy-target-class="true" ></aop:>Copy the code

Therefore, before creating a dynamic proxy, parse the annotations or configuration to see if the proxy-target-class parameter is configured. If this parameter is configured and its value is true, a Cglib proxy object is created. Otherwise, create a JDK proxy object. Typically, we use JDK proxy objects defined by Spring itself. NewProxyInstance (classLoader, proxiedInterfaces, this); Creating a Dynamic proxy

There is an invoke() method in the JDKDynamicAopProxy proxy class. The invoke method is the one invoked when the proxy object’s method is executed.

This method executes a method defined in the target class through a reflected method.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 

}
Copy the code

3. Invoke the dynamic proxy.

Call There is a very classic call logic — the call chain.

 

As shown in the figure above, the logic of the call chain is to call the dynamic proxy method, such as div(arg1, arg2), execute the first advice in the call chain advisor1, then the first advice calls the second advice, execute the second advice, and so on, when all the advice has been executed, Call the target method div(arg1, arg2) and return the execution result. Let’s look at the logical implementation of the code.

The following code entry to call the dynamic proxy:

 

public class LxlMainClass { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); Calculate calculate = (Calculate) ctx.getBean("lxlCalculate"); / * * * the above calculate, is returned by the dynamic proxy class * when calling the following methods of div, actually call is JdkDynamicAopProxy invoke (...). Method */ calculate.div(2, 4); ProgramCalculate programCalculate = (ProgramCalculate) ctx.getBean("lxlCalculate"); String s = programCalculate.toBinary(5); System.out.println(s); }}Copy the code

The Calculate object we get in the main method is actually a dynamically proxy-generated object. When the calculate.div(2, 4) method is called, the invoke() method of the dynamic proxy is called.

@Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; // Set the proxy context Boolean setProxyContext = false; / / target source: is the target of the agent class TargetSource TargetSource = this. Advised. TargetSource; Object target = null; try { if (! this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } else if (! this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } // If the method class is an interface && is an assignable method of the Advised type else if (! this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; If (this. Advised. ExposeProxy) {/ / variable expose proxy objects to the thread. OldProxy = AopContext. SetCurrentProxy (proxy); // Set the proxy context to true setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. // getTarget = targetsource.gettarget (); Class<? > targetClass = (target ! = null ? target.getClass() : null); // Convert all of aop's Advisors into interceptors, and convert advisor objects into interceptor objects by calling /** * in the chain of responsibility mode. * Mainly because of the chain of responsibility call. As mentioned earlier, to make chain of responsibility calls, they need to have a common method. * When converted to interceptor, the common method is invoke(). * beforeAdivsor, afterAdvisor, returningAdvisor, throwingAdvisor. Only the returningAdvisor and throwingAdvisor are converted into Interceptor. * Because the beforeAdvisor and adgerAdvisor themselves implement the Interceptor interface */ List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); / / is empty if the interceptor chain (chain. IsEmpty ()) {/ / by reflecting directly calls the target method execution Object [] argsToUse = AopProxyUtils. AdaptArgumentsIfNecessary (method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else {/ / create a method invocation interceptors the MethodInvocation invocation = new ReflectiveMethodInvocation (proxy, target, method, args, targetClass, chain); RetVal = invocation. Proceed (); } // Massage return value if necessary. Class<? > returnType = method.getReturnType(); if (retVal ! = null && retVal == target && returnType ! = Object.class && returnType.isInstance(proxy) && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType ! = Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target ! = null && ! targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); }}}Copy the code

 

There are two important steps:

Step 1: Convert the matching Advisor into an Interceptor

Step 2: Invoke the responsibility chain to execute various notifications

 

Take the first step: convert the matching Advisor object into an Interceptor interceptor object. Why convert the Advisor to an Interceptor interceptor?

Because you’re going to call the chain of responsibility. As mentioned earlier, to make chain of responsibility calls, they need to have a common method. Once interceptor is converted, the common method is invoke().

@Override public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class<? > targetClass) { // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisors [] advisors = config.getAdvisors(); List<Object> interceptorList = new ArrayList<>(advisors.length); Class<? > actualClass = (targetClass ! = null ? targetClass : method.getDeclaringClass()); Boolean hasIntroductions = null; for (Advisor advisor : Advisors) {/** * If the Advisor is of the PointcutAdvisor type */ if (Advisor instanceof PointcutAdvisor) {// Add it recess. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; / / annotation configuration information is a profiteer or match the target class advisor if the tangent point of expression (config. IsPreFiltered () | | pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); boolean match; if (mm instanceof IntroductionAwareMethodMatcher) { if (hasIntroductions == null) { hasIntroductions = hasMatchingIntroductions(advisors, actualClass); } match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); } else { match = mm.matches(method, actualClass); } if (match) {convert advice to MethodInterceptor, MethodInterceptor[] interceptors = registry. GetInterceptors (Advisor); if (mm.isRuntime()) { // Creating a new object instance in the getInterceptors() method // isn't a problem as we normally cache created chains. for (MethodInterceptor interceptor : Interceptors) {// Assemble the MethodInterceptor interceptor and MethodMatcher into a new object, interceptorList.add(new) InterceptorAndDynamicMethodMatcher(interceptor, mm)); }} else {interceptorList.addAll(interceptorList.aslist (interceptors)); }}}} else if (Advisor instanceof cloud Advisor) {// If the advisor is a cloud Advisor type cloud Advisor = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); }} else {// Other types of advisor Interceptor[] interceptors = Registry. GetInterceptors (Advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; }Copy the code

 

The most important method here is Registry.getInterceptors (Advisor). The getInterceptors(Advisor) loop through the advisors and convert each advisor into an Interceptor. This is a concrete implementation of converting the Advisor into an Interceptor.

Let’s look at the source code and logic

@Override public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList<>(3); Advice advice = advisor.getAdvice(); If (advice instanceof MethodInterceptor) {// If advice already implements a MethodInterceptor interface, Add interceptors. Add ((MethodInterceptor) advice); } for (AdvisorAdapter adapter : This. Adapters) {// Check whether it is an advice of the specified type. If (Adapter.supportsadvice (advice)) {// If yes, convert it to an Interceptor of the corresponding type interceptors.add(adapter.getInterceptor(advisor) ); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } return interceptors.toArray(new MethodInterceptor[0]); }Copy the code

 

Adapter. supportsAdvice(advice) Check whether the advice is a specified type of adapter

  • MethodBeforeAdviceAdapter: pre inform adapter
  • Rear AfterReturningAdviceAdapter: | back inform adapter
  • SimpleBeforeAdviceAdapter: simpler pre inform adapter
  • ThrowsAdviceAdapter: Exception notification adapter

The adapter pattern is used to match different types of notifications. Then call Adapter.getInterceptor (Advisor) to build the Advisor into the Interceptor.

There are usually beforeAdivsor, afterAdvisor, returningAdvisor, and throwingAdvisor types of notifications. Only the returningAdvisor and throwingAdvisor will be converted into Interceptor. Because the beforeAdvisor and afterAdvisor themselves implement the Interceptor interface.

Return all advisors converted into interceptors and placed in the collection of Interceptors.

 

Next, the chain of responsibility call is performed. There are two main ideas for chain of responsibility invocation

  1. Recursive calls

  2. All advisors eventually make it interceptor and rewrite the invoke() method.

Take a look at the source code

@Override @Nullable public Object proceed() throws Throwable { // We start with an index of -1 and increment early. // If it's the last interceptor, , directly executed. InvokeJoinpoint () method if (this. CurrentInterceptorIndex = = this. InterceptorsAndDynamicMethodMatchers. The size () - 1) { return invokeJoinpoint(); } / / remove interceptorsAndDynamicMethodMatchers Object Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); / / if it is InterceptorAndDynamicMethodMatcher type 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()); Matches (this.method, targetClass, this.arguments)) {// Matchs () { Invoke () return dm.interceptor.invoke(this); } else {// Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. Call return proceed(), the next interceptor in the chain; } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. Pointcuts are statically evaluated before constructing this object. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this ); }}Copy the code

 

InterceptorsAndDynamicMethodMatchers stored here is all matched to the advisor. After fetching the Advisor in order and converting it into a MethodInterceptor, its invoke(this) method is called, passing the current object, and the proceed() method is called here on Invoke (this). Cycle call. Take interceptorsAndDynamicMethodMatchers advisor and out until the last advisor. Calling proceed() again specifies that the target method is invoked.

A total of six advisor interceptorsAndDynamicMethodMatchers

The specific call is shown below:

That is the whole process of invoking AOP. The content is still a lot of time to digest