I. Basic theoretical knowledge

Here’s a primer on AOP;

AOP: Directly popularly understood as section-oriented programming; There’s a lot on the web about what aspect programming is, but I won’t go into that here; The real purpose of the aspect is to use a very loose coupling to add a series of enhanced logic processing in the pre-execution, post-execution, exception-time, and other scenarios on an already completed functional logic;

As far as AOP is concerned, it is implemented by dynamic proxy. Here’s a look at some of the core concepts of AOP:

  • Aspect – the Aspect

The emphasis is on modularity; This concern may crosscut multiple objects; Transaction management is a typical example of the application of aspects in Java enterprise applications. In Spring Aop, aspects can be implemented using the plain class-based schema-based approach or the @aspect annotation in plain classes;

  • Join point — Join point

In Spring AOP a join point represents the execution of a method;

  • Notice – Advice

An action performed at a specific join point in the section; Notice with around, before, after and other types; In many AOP models, including Spring, the notification model is basically implemented through interceptors;

  • Target object — Target

Refers to the target object to be enhanced, the class object containing the main business logic;

  • Point of tangency, Pointcut

Predicate logic for matching link points; Notifications are associated with pointcuts through expressions and run at join points that satisfy this pointcut, i.e., when a particular named method is executed; The matching of pointcut expressions and join points is the core of AOP, and AspectJ pointcut semantics is used by default in Spring.

  • Weave – has

The process of inserting notifications into join points is called weaving;

  • Introduction – Introductions

Additional interfaces and implementations can be dynamically introduced into targetClass;

Second, AOP annotation

Annotations are recommended in SpringBoot, so we’re going to start with annotations,

  • @EnableAspectJAutoProxy

This annotation has the same functionality as the XML tag AOP: AspectJ-AutoProxy;

  • @Aspect

Identifies the current class as an aspect processing class.

  • @Pointcut

Identify the enhancement point of the tangent point;

  • @Before

Pre-processing, the processing that needs to be performed before the pointcut method executes;

  • @After

Post-processing, which needs to be performed after the pointcut method;

  • @AfterReturning

Return processing, the processing that needs to be performed after the pointcut method returns data;

  • @AfterThrowing

Exception handling, which needs to be performed when an exception occurs in the execution of the pointcut method;

  • @DeclareParents

Into a class, the class can join a dynamic object, using the ApplicationContext. GetBean can use the join object to receive and execute method, this method USES less, in addition this method introduced later will not be front rear processing such as enhancement;

Spring AOP and AspectJ

In fact, this question can really defeat the hero, many conceptual things are very vague, while sorting out this article to do an elaboration of the relevant knowledge;

3.1) Spring AOP

Aop is implemented in Spring through dynamic proxies. If it is an interface, use the dynamic proxy provided by JDK to implement it. If there is no interface, use CGLIB.

The source code for CGLIB and ASM was included directly in SpringCore after Spring3.2, which is why these two dependencies were introduced without display;

AOP in Spring relies on the IOC container to manage it;

AspectJ support is also available in Spring, but only with AspectJ pointcut parsing and matching: AspectJ expressions;

Note: SpringAOP is based on a proxy implementation. When the container is started, instances of the proxy need to be generated and the stack depth is increased in method calls, so SpringAOP does not perform as well as AspectJ.

We declare an early event listener and event. We don’t need the developer to manually call the publishEvent method to publish the event. Spring publishes the event. Big ye don’t worry, take your time, look down;

3.2) AspectJ

From the Eclipse Foundation (www.eclipse.org/aspectj)

AspectJ is statically woven, that is, implemented by modifying code; The specific weaving time is as follows:

  • Compile time weaving: for example, if a class uses AspectJ to add an attribute, and another class references it, the scenario needs to be woven at Compile time. Otherwise, the reference class cannot be compiled.
  • The word for weaving post-compile is the part where you generate a class file or a Jar package and you need to improve the weaving.
  • Load-time weaving: weaving at the time of class loading. The usual weaving methods are as follows:
    • This is a common approach. Load the class before it is woven into the JVM, so that you can customize the behavior at load time.
    • Specify the agent method provided by AspectJ (-javaAgent: [jar path]) at JVM startup. This method is not commonly used.

AspectJ does a lot of things that Spring AOP can’t do, and is a complete solution to AOP programming; The difference is that Spring AOP addresses the most common AOP requirement in enterprise development (method weaving), rather than being a complete solution for AOP programming;

Note: AspectJ does the weaving before the actual code runs, so you could argue that it generates classes with no overhead; In addition, if you want to use AspectJ, you must use AspectJ’s own compiler and parser, so development costs increase;

3.3) conclusion

In Spring AOP only provides AspectJ support, only uses the annotations of the pointcut part, and continues to use some AspectJ concepts, because Spring as an ecology, it is more concerned with meeting and solving the actual needs of enterprise application development, simplification and practical needs. Not AOP for AOP’s sake;

The beginning of Spring AOP

Spring 1.2: Interface-based configuration, the earliest Spring AOP was based entirely on a few specific interfaces;

Spring 2.0: Schema-based configuration. After 2.0, XML is used for configuration and management (tags).

Spring 2.0: @AspectJ configuration, using annotations for configuration, is very convenient to use. Note that @AspectJ has nothing to do with real AspectJ.

Example code is as follows:

Business processes used in the test:

public class TestManager {

    /** **@date: 2021/4/200020 9:23 PM */
    public void add(int a, int b){
        System.out.println("Parameter operation result:"+ (a+b)); }}Copy the code

Pre-notification enhancement class, note MethodBeforeAdvice this interface:

public class TestBeforeAdvice implements MethodBeforeAdvice {
    /**
     * Callback before a given method is invoked.
     *
     * @param method method being invoked
     * @param args   arguments to the method
     * @param target target of the method invocation. May be {@code null}.
     * @throws Throwable if this object wishes to abort the call.
     *                   Any exception thrown will be returned to the caller if it's
     *                   allowed by the method signature. Otherwise the exception
     *                   will be wrapped as a runtime exception.
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Preintercept -- execute object:"+ target + "; Execution method:"+ method.getName() + "; The ginseng,"+ Arrays.asList(args)); }}Copy the code

Surround the enhanced class, noting the MethodInterceptor interface:

public class TestInterceptor implements MethodInterceptor {

    /**
     * Implement this method to perform extra treatments before and
     * after the invocation. Polite implementations would certainly
     * like to invoke {@link Joinpoint#proceed()}.
     *
     * @param invocation the method invocation joinpoint
     * @return the result of the call to {@link Joinpoint#proceed()};
     * might be intercepted by the interceptor
     * @throws Throwable if the interceptors or the target object
     *                   throws an exception
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Surround interception -- execute object:"+getClass()+"Parameter:"+ Arrays.asList(invocation.getArguments()));
        /* * Note that the invocation. Proceed () is the duty chain mode; * is called recursively down to the last node * */
        final Object proceed = invocation.proceed();
        System.out.println("Surround interception -- execute object:"+getClass()+"Parameter:"+ Arrays.asList(invocation.getArguments()));
        returnproceed; }}Copy the code

The configuration class:

public class AopConfig {

    /** * injection test object **@date: 2021/4/200020 8:54 PM */
    @Bean
    public TestManager testManagerBean(a) {
        return new TestManager();
    }

    /** *@date: 2021/4/200020 8:54 PM */
    @Bean
    public TestBeforeAdvice testBeforeAdvice(a) {
        return new TestBeforeAdvice();
    }

    /** * injects surround notification **@date: 2021/4/200020 8:59 PM */
    @Bean
    public TestInterceptor testInterceptor(a) {
        return new TestInterceptor();
    }

    /** * ProxyFactoryBean injection ProxyFactoryBean injection ProxyFactoryBean ProxyFactoryBean is a class that can produce proxy beans, as you might expect from its name. This approach is one of the earliest implementations of AOP (before the introduction of AspectJ). It's easy to see that you can only intercept and enhance one class at a time. If you want to enhance multiple classes, you need to create multiple ProxyFactoryBeans * which are very cumbersome to use * You need to pay special attention to the ProxyFactoryBean. As discussed in the previous section, this is a special class. Instead of loading a ProxyFactoryBean when the IOC container is loaded, it is the class returned by calling its getObject method@date: 2021/4/200020 9:06 PM */
    @Bean
    public ProxyFactoryBean proxyFactoryBean(a) {
        final ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        // Set the handler that needs to be enhanced. After this is set, the execution is called in sequence through the responsibility chain mode;
        proxyFactoryBean.setInterceptorNames("testBeforeAdvice"."testInterceptor");
        // Specify the Bean to intercept
        proxyFactoryBean.setTarget(testManagerBean());
        returnproxyFactoryBean; }}Copy the code

Start the class:

public class TestMain {
    public static void main(String[] values){
        final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        final TestManager proxyFactoryBean = annotationConfigApplicationContext.getBean("proxyFactoryBean", TestManager.class);
        System.out.println("The object called is:"+proxyFactoryBean.getClass());
        proxyFactoryBean.add(1.2); }}Copy the code

Final result:

The called object is:class xuexi.aop.test.TestManager$$EnhancerBySpringCGLIB$$455ba05cPreintercept -- Execute object:xuexi.aop.test.TestManager@ 4c9f8c13; Execution method:add; Input parameters: [1, 2] Surround interception -- execute object:class xuexi.aop.test.TestInterceptorParameter: [1, 2] Parameter operation result: 3 Surround interception -- execution object:class xuexi.aop.test.TestInterceptorParameter: [1, 2]Copy the code

In Spring, this type of Bean is special. When the IOC container reloads, it does not load itself, but calls its getObject method to get the Bean. So here’s the core logic for generating the proxy;

/** * Return a proxy. Invoked when clients obtain beans from this factory bean. * Create an instance of the AOP proxy to  be returned by this factory. * The instance will be cached for a singleton, and create on each call to * {@code getObject()} for a proxy.
 * @return a fresh AOP proxy reflecting the current state of this factory
 */
@Override
@Nullable
public Object getObject(a) throws BeansException {
   /* * See my previous section for the responsibility chain Demo; * * /
   initializeAdvisorChain();
   if (isSingleton()) {
      /* * the dynamic proxy object * */ has been created
      return getSingletonInstance();
   }
   else {
      if (this.targetName == null) {
         logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
               "Enable prototype proxies by setting the 'targetName' property.");
      }
      returnnewPrototypeInstance(); }}Copy the code

In the source code initializeAdvisorChain method is a typical responsibility chain call processing; Call the enhancers added through the setInterceptorNames method, and call them sequentially; MethodInterceptor, MethodBeforeAdvice, and other built-in interfaces have the same parent class. MethodInterceptor, MethodBeforeAdvice, and other built-in interfaces have the same parent class. (PS: If not the same Lao tzu, random door will have an accident)

Now modify the previous test business processing class:

public class TestManager {

    /** * function description: test method -- addition *@date: 2021/4/200020 9:23 PM */
    public void add(int a, int b){
        System.out.println("Parameter operation result:"+ (a+b));
    }

    /** * function description: test method -- subtraction *@date: 2021/4/200020 9:23 PM */
    public void subtraction(int a, int b){
        System.out.println("Parameter operation result:"+ (a-b)); }}Copy the code

Modify the boot method:

public class TestMain {
    public static void main(String[] values){
        final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        final TestManager proxyFactoryBean = annotationConfigApplicationContext.getBean("proxyFactoryBean", TestManager.class);
        System.out.println("The object called is:"+proxyFactoryBean.getClass());
        proxyFactoryBean.subtraction(1.2); }}Copy the code

The results are as follows:

Call object is: class xuexi. Aop. Test. The TestManager $$$$455 ba05c EnhancerBySpringCGLIB front intercept - perform object: xuexi. Aop) test. The TestManager @ 4 c9f8c13; Implementation method: subtraction; Into arguments: [1, 2] around intercept - perform object: class xuexi, aop) test. The TestInterceptor parameters: [1, 2] parameter computation results: - 1 around the intercept - perform object: The class xuexi. Aop. Test. TestInterceptor parameters: [1, 2]Copy the code

Thus it can be seen that the granularity of AOP in this way can only be controlled to the class, not to the method; So how to control the granularity in a more delicate way? (Or that old saying: grandpa, you don’t worry!) Spring is paid more attention to enterprise applications demand, so developers are provided with an enhanced processing categories: NameMatchMethodPointcutAdvisor; (PS: Please remember this last word Advisor; Very important! Very important! Very important! Important things say three times;) Modify the configuration class:

public class AopConfig {

    /** * injection test object **@date: 2021/4/200020 8:54 PM */
    @Bean
    public TestManager testManagerBean(a) {
        return new TestManager();
    }

    /** *@date: 2021/4/200020 8:54 PM */
    @Bean
    public TestBeforeAdvice testBeforeAdvice(a) {
        return new TestBeforeAdvice();
    }

    /** * injects surround notification **@date: 2021/4/200020 8:59 PM */
    @Bean
    public TestInterceptor testInterceptor(a) {
        return new TestInterceptor();
    }

    @Bean
    public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(a){
        final NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
        // Add the enhancer
        nameMatchMethodPointcutAdvisor.setAdvice(testBeforeAdvice());
        // Add the method name
        nameMatchMethodPointcutAdvisor.setMappedName("subtraction");
        return nameMatchMethodPointcutAdvisor;
    }

    @Bean
    public ProxyFactoryBean proxyFactoryBean(a) {
        final ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        // Set the handler that needs to be enhanced. After this is set, the execution is called in sequence through the responsibility chain mode;
        proxyFactoryBean.setInterceptorNames("nameMatchMethodPointcutAdvisor");
        // Specify the Bean to intercept
        proxyFactoryBean.setTarget(testManagerBean());
        returnproxyFactoryBean; }}Copy the code

Modify the startup class:

public class TestMain {
    public static void main(String[] values){
        final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        final TestManager proxyFactoryBean = annotationConfigApplicationContext.getBean("proxyFactoryBean", TestManager.class);
        System.out.println("The object called is:"+proxyFactoryBean.getClass());
        proxyFactoryBean.add(1.2);
        proxyFactoryBean.subtraction(1.2); }}Copy the code

Execution Result:

Parameter calculation results: 3 front intercept - perform object: xuexi. Aop) test. The TestManager @ a9cd3b1; Implementation method: subtraction; Input parameter: [1, 2] Parameter operation result: -1Copy the code

You can see from the results that there are no enhancements to the method add; On the contrary, the subtraction method was enhanced;

There are many types of Advisors, roughly as follows:

  • RegexpMethodPointcutAdvisor: according to the regular expression match;
  • NameMatchMethodPointcutAdvisor: according to the method name match;
  • DefaultBeanFactoryPointcutAdvisor: based on the XML configuration Advisor, such as: aop: before;
  • InstantiationModelAwarePointcutAdvisorImpl: annotation parsing Advisor, such as: @ Before, @ After and so on;

The reason why this Advisor is important is that in the current development, Spring will encapsulate annotation notification methods such as @before and @After into a corresponding Advisor, which is a unified Advisor object: InstantiationModelAwarePointcutAdvisorImpl; Have a AspectJExpressionPointcut object in the object, by the object to parse the tangent point; The example above we are manually specify a NameMatchMethodPointcutAdvisor, so in the Spring the scanning of the encountered in the process of the method or class time will be encapsulated into different Advisor according to is;

Having said that, it still doesn’t completely address the urinality of creating a ProxyFactoryBean for each class; So how to solve this problem? This is also a requirement for enterprise application development, and Spring certainly won’t ignore it; Spring also introduces the concept of an AutoProxyCreator; The AutoxyCreator leverages the BeanPostProcessor concept of beans;

PS: Bean backend processor concept need not I again here blind force, do not understand my previous article;

Now add an interface (custom, a unified interface for multiple classes with the same processing) :

public interface ITestManager {
    /** * function description: add operation *@date: 2021/4/200020 10:36 PM */
    void add(int a, int b);
    /** ** *@date: 2021/4/200020 10:36 PM */
    void subtraction(int a, int b);
}
Copy the code

Modify the test business class:

public class TestManager implements ITestManager {

    /** **@date: 2021/4/200020 9:23 PM */
    @Override
    public void add(int a, int b){
        System.out.println("Parameter operation result:"+ (a+b));
    }

    /** **@date: 2021/4/200020 9:23 PM */
    @Override
    public void subtraction(int a, int b){
        System.out.println("Parameter operation result:"+ (a-b)); }}Copy the code

Create a test business class to see the effect

public class TestManagerTwo implements ITestManager {

    /** **@date: 2021/4/200020 9:23 PM */
    @Override
    public void add(int a, int b){
        System.out.println("Parameter operation result:"+ ((a+b)*10));
    }

    /** **@date: 2021/4/200020 9:23 PM */
    @Override
    public void subtraction(int a, int b){
        System.out.println("Parameter operation result:"+ ((a-b)*10)); }}Copy the code

Modify the configuration class

public class AopConfig {

    /** * injection test object **@date: 2021/4/200020 8:54 PM */
    @Bean
    public ITestManager testManager(a) {
        return new TestManager();
    }

    /** * injection test object **@date: 2021/4/200020 8:54 PM */
    @Bean
    public ITestManager testManagerTwo(a) {
        return new TestManagerTwo();
    }

    /** *@date: 2021/4/200020 8:54 PM */
    @Bean
    public TestBeforeAdvice testBeforeAdvice(a) {
        return new TestBeforeAdvice();
    }

    /** * injects surround notification **@date: 2021/4/200020 8:59 PM */
    @Bean
    public TestInterceptor testInterceptor(a) {
        return new TestInterceptor();
    }

    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator(a){
        final BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        // The names of all the beans for which the proxy needs to be created, using wildcards here
        beanNameAutoProxyCreator.setBeanNames("testManager*");
        // Set the interceptor enhancer. Note that the enhancers are added in order, according to the order of input
        beanNameAutoProxyCreator.setInterceptorNames("testBeforeAdvice"."testInterceptor");
        returnbeanNameAutoProxyCreator; }}Copy the code

Last modify startup class:

public class TestMain {
    public static void main(String[] values){
        final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        ITestManager testManager = (ITestManager) annotationConfigApplicationContext.getBean("testManager");
        System.out.println("The object called is:"+testManager.getClass());
        testManager.add(1.2);
        testManager.subtraction(1.2);

        testManager = (ITestManager) annotationConfigApplicationContext.getBean("testManagerTwo");
        System.out.println("The object called is:"+testManager.getClass());
        testManager.add(1.2);
        testManager.subtraction(1.2); }}Copy the code

The execution result

Front intercept - perform object: xuexi. Aop. Test. The TestManager @ 5 b0abc94; Execute method: add; Into arguments: [1, 2] around intercept - perform object: class xuexi, aop) test. The TestInterceptor parameters: [1, 2] parameter computation results: 3 surround intercept - perform object: Class xuexi. Aop. Test. TestInterceptor parameters: [1, 2] front intercept - perform object: xuexi. Aop) test. The TestManager @ 5 b0abc94; Implementation method: subtraction; Into arguments: [1, 2] around intercept - perform object: class xuexi, aop) test. The TestInterceptor parameters: [1, 2] parameter computation results: - 1 around the intercept - perform object: Class xuexi. Aop. Test. TestInterceptor parameters: [1, 2] call object is: class com. Sun. Proxy. $Proxy8 front intercept - perform object: Xuexi. Aop. Test. TestManagerTwo @ 75 c072cb; Execute method: add; Into arguments: [1, 2] around intercept - perform object: class xuexi, aop) test. The TestInterceptor parameters: [1, 2] parameter computation results: 30 surrounding the intercept - perform object: Class xuexi. Aop. Test. TestInterceptor parameters: [1, 2] front intercept - perform object: xuexi. Aop) test. TestManagerTwo @ 75 c072cb; Implementation method: subtraction; Into arguments: [1, 2] around intercept - perform object: class xuexi, aop) test. The TestInterceptor parameters: [1, 2] parameter computation results: - 10 surrounding the intercept - perform object: The class xuexi. Aop. Test. TestInterceptor parameters: [1, 2]Copy the code

See if they’ve all been represented;

Fifth, AOP annotation method implementation principle

5.1) Basic process

Looking back at the examples above, doesn’t it feel pretty close to what it looks like today? Of course, there are still some differences. The full annotation method is used now. As mentioned above, different advisors will eventually be generated based on annotations when the IOC container is loaded. The general processing flow in Spring is as follows:

Here’s a kickback to the “Bean Postprocessor” section of the previous topic; So in Spring IOC is the core, the cornerstone, and everything else is done through open extension points; That is, with efficient expansion, easy to develop and maintain;

5.2) Load logic

To be honest, I have been looking for the content of this piece for a long time. Although it is called Bean postprocessor, I have been unable to find the entrance of injection. I have been twisting my soul and want to hit the keyboard for several times. Accidentally found a document, opened up a new emperor of Heaven, is still continuing in-depth research;

The spring-autoconfigure-metadata.properties file is a configuration file used for Spring auto-assembly. It contains the following paragraph:

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.ConditionalOnClass=org.aspectj.lang.annotation.Aspect,or g.aspectj.lang.reflect.Advice,org.aspectj.weaver.AnnotatedElement,org.springframework.context.annotation.EnableAspectJAu toProxy

Here’s a bit more verbose: Bootstrap annotation: @springBootApplication — > Bootstrap annotation: @enableAutoConfiguration — > Bootstrap import handler class: AutoConfigurationImportSelector – > method: selectImports

Last EnableAspectJAutoProxy note, this is an annotation of @ Import introduces a AspectJAutoProxyRegistrar in class;

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) {
      /* * Register a container with the name: * org. Springframework. Aop. Config. InternalAutoProxyCreator AnnotationAwareAspectJAutoProxyCreator object * to follow up and you can see; * this is a rear processor object, is used to resolve using @aspectj annotations modified class, due to this object implements InstantiationAwareBeanPostProcessor interface, * So the post-handler is called first in the createBean method; * This object also implements the BeanPostProcessor interface, so it is called once after initialization
      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

This class inherits ImportBeanDefinitionRegistrar instructions with registered function is a Bean definition registry; To register a AnnotationAwareAspectJAutoProxyCreator object in the Spring container, check out this prick silk class structure

Go against the AnnotationAwareAspectJAutoProxyCreator very, And to achieve the InstantiationAwareBeanPostProcessor, SmartInstantiationAwareBeanPostProcessor, BeanPostProcessor the three very important interface;

In the way to create Bean into the createBean priority will be called when one rear InstantiationAwareBeanPostProcessor type of processor (note: Again, the afterhandler is called nine times during the entire execution of the createBean method), which is when classes with @AspectJ annotations begin to be processed. It is this call that wraps the @before, @After annotations in the class that meet the criteria into an Advisor object;

Rear SmartInstantiationAwareBeanPostProcessor this type of processor is specially used for processing of circular dependencies, needless to explain; BeanPostProcessor is a post-processor interface that is called after initialization. It starts to determine whether the current class meets the criteria for proxy enhancement at the same time. If so, it starts to create dynamic proxy classes.

5.3) First call

The first call is after entering the createBean InstantiationAwareBeanPostProcessor. PostProcessBeforeInstantiation method;

The org. Springframework. Aop. Framework. Autoproxy. AbstractAutoProxyCreator# postProcessBeforeInstantiation method source code before the chapter has made a detailed instructions This is just to the point

Pay particular attention to the shouldSkip method, which normally returns false directly; But combining AnnotationAwareAspectJAutoProxyCreator this class, it is not so simple, because in this class to rewrite shouldSkip method, finished here on the object of the @aspectj parsing; Here’s the shouldSkip AnnotationAwareAspectJAutoProxyCreator method (org. Springframework. Aop. Aspectj. Autoproxy. AspectJAwareAdvisorAu ToProxyCreator# shouldSkip)

@Override
protected boolean shouldSkip(Class
        beanClass, String beanName) {
   /** * Find the candidate Advisors(notifier object) */
   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
@Override
protected List<Advisor> findCandidateAdvisors(a) {
   // Find out which advisors are related to the transaction
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Encapsulate the aspect-related information into an Advisor
   if (this.aspectJAdvisorsBuilder ! =null) {
      // Get all the section cache objects in the container
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   // Return all our notifications
   return advisors;
}
Copy the code

This buildAspectJAdvisors method is the core of the process, here is just a list of the general, call chain abnormal deep, want to challenge a hold in the world record friends can follow up;

/** * retrieve all section information from the container and save it in the cache *@return the list of {@link org.springframework.aop.Advisor} beans
 * @see #isEligibleBean
 */
public List<Advisor> buildAspectJAdvisors(a) {
   /** * where aspectNames are our class-level cache. The user caches resolved aspect information */
   List<String> aspectNames = this.aspectBeanNames;
   // The cache field aspectNames has no value to indicate that parsing the aspect is triggered when the first singleton bean is instantiated
   if (aspectNames == null) {
      // A DCL check is performed
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            // Used to hold all parsed Advisors collection objects
            List<Advisor> advisors = new ArrayList<>();
            // A collection of names used to hold sections
            aspectNames = new ArrayList<>();
            /** * The aop function passes in the Object Object, which means to fetch all the component names in the container, and then iterate over them one by one. This process is very performance consuming, so spring will add the cache to store the aspect information. * Note: the transaction function is different, the function of the transaction module is to directly go to the container to obtain the Advisor type, the selection is small, cut does not consume performance. So * Spring has no caching in the transaction module to hold our transaction-related advisor */
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true.false);
            // Iterate over the names of all the beans we fetch from the IOC container
            for (String beanName : beanNames) {
               if(! isEligibleBean(beanName)) {continue;
               }
               // Use beanName to get the corresponding class object from the containerClass<? > beanType =this.beanFactory.getType(beanName);
               if (beanType == null) {
                  continue;
               }
               // Determine whether the plane is cut based on the class object
               if (this.advisorFactory.isAspect(beanType)) {
                  /* * add to cache; * Note here that isAspect also excludes proxy objects generated by AspectJ, since all proxy objects generated by AspectJ start with * */ ajC $
                  aspectNames.add(beanName);
                  // Build the beanName and class objects into one AspectMetadata
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {

                     // Build an instance factory for section annotations
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                     /* * To actually get our instance factory * getAdvisors will get all the methods in the aspect class annotated by @before, @after, etc. * and parse them into a wrapper class of Type Advisor * */
                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                     // Add to the cache
                     if (this.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;
            returnadvisors; }}}if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   /** * To actually create a section, we don't need to parse it and go directly to the cache */
   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);
         // Parse the core method getAdvisors for each annotation
         advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}return advisors;
}
Copy the code

PS: A buddy asked me about so many annotations before, do they parse in order? What’s the order? At that time, I was really not sure. Later, when I saw the source code here, I found that it was really sequential, and the specific description was as follows: Org. Springframework. Aop. Aspectj. The annotation. AbstractAspectJAdvisorFactory# ASPECTJ_ANNOTATION_CLASSES this property, Start processing at once according to the order of the attributes;

private static finalClass<? >[] ASPECTJ_ANNOTATION_CLASSES =newClass<? >[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};Copy the code

OK! I’ll stop there;