AOP-implementation principle

AOP-implementation principle

1. Basic theoretical knowledge

Here is the basic knowledge of AOP;

AOP: A straightforward and popular understanding is aspect-oriented programming; there are many aspects of aspect-oriented programming on the Internet, so I won t elaborate on it here; in fact, the real role of aspects is to use very loosely coupled logic on a certain completed function. Ways to add a series of enhanced logic processing in scenarios such as before, after, and exceptions;

As far as AOP is concerned, it is achieved through dynamic proxy; here we need to understand several core concepts of AOP:

  • Aspect-Aspect

The key is to focus on modularity; this focus may cross-cut multiple objects; transaction management is a typical application example of the aspect in Java enterprise applications. Aspects in Spring Aop can be implemented using common classes based on schema-based approach or common classes with @Aspect annotations;

  • Connection point-Join point

In Spring AOP, a connection point represents the execution of a method;

  • Notification-Advice

Actions performed at a specific connection point of the aspect; notifications have multiple types such as around, before, after; 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 cut-Pointcut

Match the assertion logic of the link point; the notification and the point of contact are related by expressions, and run on the connection point that satisfies the point of contact, that is: when a specific name method is executed; the match of the point ofcut expression and the connection point is At the core of AOP, AspectJ pointcut semantics are used by default in Spring;

  • Weaving-Weaving

The process of cutting notifications into the connection point is called weaving;

  • Introduction-Introductions

Other interfaces and implementations can be dynamically introduced into targetClass;

2. AOP annotation description

It is recommended to use the annotation method in SpringBoot, then we will start with the annotation method here,

  • @EnableAspectJAutoProxy

This annotation has the same function as the XML tag aop: aspectj-autoproxy ;

  • @Aspect

Identifies that the current class is an aspect processing class;

  • @Pointcut

Identify the enhancement point of the cut point;

  • @Before

Pre-processing, the processing that needs to be performed before the point-cut method is executed;

  • @After

Post-processing, processing that needs to be performed after the point-cut method;

  • @AfterReturning

Return processing, the processing that needs to be performed after the point-cut method returns the data;

  • @AfterThrowing

Exception handling, the processing that needs to be executed when an exception occurs in the execution of the pointcut method;

  • @DeclareParents

Introduce a class. This class can dynamically add an object. When using ApplicationContext.getBean, you can use this added object to receive and execute methods. This method is less used, and after this method is introduced, it will not be Post-positioning and other processing for enhancement;

3. Spring AOP and AspectJ

In fact, this question can really stump heroes. Many conceptual things are very vague. When finishing this article, I will explain this piece of related knowledge by the way;

3.1) Spring AOP

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

After Spring 3.2, the source code of CGLIB and ASM is directly included in SpringCore, which is why these two dependencies do not need to be displayed;

In Spring, AOP needs to rely on the IOC container for management;

AspectJ support is also provided in Spring, but only the pointcut analysis and matching of AspectJ is used, namely: AspectJ expression;

Note: Spring's AOP is implemented based on a proxy. When the container starts, an instance of the proxy needs to be generated, and the depth of the stack is also increased in method calls, so the performance of SpringAOP is not as good as AspectJ;

The point here is to declare an early event listener and event. Developers do not need to manually call the publishEvent method to publish events. Spring will complete the publishing. Then when is Spring released? Uncle, don't worry, take your time and look down;

3.2) AspectJ

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

AspectJ belongs to static weaving, that is: achieved by modifying the code; the specific weaving timing is as follows:

  • Compile-time weaving: Compile-time weaving, for example: a class uses AspectJ to add an attribute, if another class references it, this scenario needs to be weaving at compile time, otherwise the referenced class cannot be compiled;
  • Post-compile weaving: Weaving after compilation. In layman's terms, the class file has been generated or the Jar package has been marked. If you need to enhance it at this time, you must use the post-compile weaving;
  • Load-time weaving: Weaving when loading classes. The methods usually used for weaving in this period are as follows:
    • 1. Customize a class loader to complete it. This is a relatively common solution. Load the woven class before it is loaded into the JVM, so that you can customize the behavior during loading;
    • 2. Specify the agent method (-javaagent: [jar path]) provided by AspectJ when the JVM is started. This method is not commonly used;

In fact, AspectJ can handle many things that Spring AOP cannot do. It can be said to be a complete solution for AOP programming; the difference is that Spring AOP is committed to solving the most common AOP requirements (method weaving) in enterprise-level development, rather than becoming an AOP programming Complete solution;

Note: AspectJ completes the weaving before the actual code runs, so everyone will say that the classes he generates have no additional overhead; in addition, if you want to use AspectJ, you must use AspectJ's own compiler parser, so the development cost increases

3.3) Summary

In Spring's AOP, only AspectJ support is provided, only the annotations of the pointcut part are used, and some AspectJ concepts are extended. Because Spring is an ecology, it is more concerned about satisfying and solving enterprise-level application development. The actual needs in, simplify and meet the actual needs, not for AOP and AOP;

4. the start of Spring AOP

Spring 1.2: Interface-based configuration, the earliest Spring AOP is completely based on several specific interfaces;

Spring 2.0: schema-based configuration, starting to use XML for configuration and management (tags) after 2.0;

Spring 2.0: @AspectJ configuration, using annotations for configuration, this method is very convenient to use, note that @AspectJ here has no relationship with the real AspectJ;

The sample code is as follows:

Business processes used in the test:

public class TestManager { /** * Function description: test method * @date : 2021/4/20 0020 9:23 PM */ public void add ( int a, int b) { System.out.println( "Parameter calculation result:" + (a+b)); } } Copy code

Pre-notification enhancement class, pay attention to the MethodBeforeAdvice 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( "Pre-interception-execution object: " + target + "; execution method:" + method.getName() + "; input parameter: " + Arrays.asList(args)); } } Copy code

Surround the enhanced class, pay attention to 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-execution object:" +getClass()+ "Parameter:" + Arrays.asList(invocation.getArguments())); /* * Note that here invocation.proceed() is the responsibility chain model; * Will continue to recursively call down until the last node * */ final Object proceed = invocation.proceed(); System.out.println( "Wrap Interception-Execution Object:" +getClass()+ "Parameter:" + Arrays.asList(invocation.getArguments())); return proceed; } } Copy code

Configuration class:

public class AopConfig { /** * Function description: Inject the test object * * @date : 2021/4/20 0020 8:54 PM */ @Bean public TestManager testManagerBean () { return new TestManager(); } /** * Function description: Inject the preprocessor * * @date : 2021/4/20 0020 8:54 PM */ @Bean public TestBeforeAdvice testBeforeAdvice () { return new TestBeforeAdvice(); } /** * Function description: Inject surround notification * * @date : 2021/4/20 0020 8:59 PM */ @Bean public TestInterceptor testInterceptor () { return new TestInterceptor(); } /** * Function description: inject the proxy factory bean * ProxyFactoryBean this bean should be able to guess that it is a class that can produce proxy beans by looking at the name * This method is the earliest implementation of AOP (when AspectJ is not introduced) * This method is a torment. It is not difficult to find that only one class can be intercepted and enhanced at a time. If you want to enhance multiple classes, you need to create multiple ProxyFactoryBeans * Very troublesome to use * Here you need to pay special attention to ProxyFactoryBean. As mentioned in the previous chapter, this is a special class. * When the IOC container is loaded, it is not the loaded ProxyFactoryBean, but the class returned by calling the getObject method. * @date : 2021/4/20 0020 9:06 PM */ @Bean public ProxyFactoryBean proxyFactoryBean () { final ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); //Set the processor that needs to be enhanced. After it is set here, it is called in turn through the responsibility chain mode during execution; proxyFactoryBean.setInterceptorNames ( "testBeforeAdvice" , "testInterceptor" ); //Specify the bean that needs to be intercepted proxyFactoryBean.setTarget(testManagerBean()); return proxyFactoryBean; } } Copy code

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 called object is:" +proxyFactoryBean.getClass()); proxyFactoryBean.add( 1 , 2 ); } } Copy code

Final Results:

Object of the call is: class xuexi . AOP . Test . TestManager $$ EnhancerBySpringCGLIB $$ 455 ba05c Pre-interceptor - execution object: xuexi . AOP . Test . TestManager @. 4 c9f8c13 ; execution method: the Add ; into the reference: [1, 2 ] Surrounding interception-execution object: class xuexi . Aop . Test . TestInterceptor Parameters: [1, 2] Parameter calculation result: 3 Surround intercept - execution object: class xuexi . AOP . Test . TestInterceptor Parameters: [1, 2] copy the code

Pay special attention to the ProxyFactoryBean object here. From the name, you can see that this object is a factory bean used to produce proxies. In Spring, this type of bean is special. When the IOC container is reloaded, it will not load itself but Call its getObject method to obtain the Bean; then the core logic of generating the proxy is here;

/** * 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 () throws BeansException { /* * Here is the responsibility chain mode to get all injected enhanced objects configured * For the demo of the chain of responsibility, please see my previous chapters; * */ initializeAdvisorChain(); if (isSingleton()) { /* * What is returned here is actually the dynamic proxy object that 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." ); } return newPrototypeInstance(); } } Copy code

The initializeAdvisorChain method in the source code is a typical chain of responsibility call processing; the enhancer added through the setInterceptorNames method is called, and it is called sequentially; when the responsibility chain uses recursive calls, it will definitely not be messy, and there must be common talents. It can be called, from which it can be inferred that specific built-in interfaces such as MethodInterceptor, MethodBeforeAdvice, etc. have the same parent class; ( PS: if it is not the same Laozi, random visits will cause trouble )

Now modify the previous test business processing class:

public class TestManager { /** * Function description: test method-addition * @date : 2021/4/20 0020 9:23 PM */ public void add ( int a, int b) { System.out.println( "Parameter calculation result:" + (a+b)); } /** * Function description: test method-subtraction * @date : 2021/4/20 0020 9:23 PM */ public void subtraction ( int a, int b) { System.out.println( "Parameter calculation result:" + (ab)); } } Copy code

Modify the startup 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 called object is:" +proxyFactoryBean.getClass()); proxyFactoryBean.subtraction( 1 , 2 ); } } Copy code

The results are as follows:

The called object is: class xuexi.aop.test.TestManager$$EnhancerBySpringCGLIB$$455ba05c Pre-interception-execution object: xuexi.aop.test.TestManager@4c9f8c13; execution method: subtraction; input parameters: [1, 2] Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Parameter calculation result: -1 Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Copy code

It can be seen that the granularity of AOP in this way can only be controlled to the class, not the method; then how to control the granularity more delicately in the method? (Still the old saying: Uncle, don t worry!) Spring pays more attention to the needs of enterprise applications, so it provides developers with an enhanced processing class: NameMatchMethodPointcutAdvisor; ( PS: Please remember this last word Advisor ; Very important! Very important! Very important! The important thing is said three times; ) Modify the configuration class:

public class AopConfig { /** * Function description: Inject the test object * * @date : 2021/4/20 0020 8:54 PM */ @Bean public TestManager testManagerBean () { return new TestManager(); } /** * Function description: Inject the preprocessor * * @date : 2021/4/20 0020 8:54 PM */ @Bean public TestBeforeAdvice testBeforeAdvice () { return new TestBeforeAdvice(); } /** * Function description: Inject surround notification * * @date : 2021/4/20 0020 8:59 PM */ @Bean public TestInterceptor testInterceptor () { return new TestInterceptor(); } @Bean public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor () { final NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor(); //Add enhancer nameMatchMethodPointcutAdvisor.setAdvice(testBeforeAdvice()); //Add the method name nameMatchMethodPointcutAdvisor.setMappedName( "subtraction" ); return nameMatchMethodPointcutAdvisor; } @Bean public ProxyFactoryBean proxyFactoryBean () { final ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); //Set the processor that needs to be enhanced. After it is set here, it is called in turn through the chain of responsibility mode during execution; proxyFactoryBean.setInterceptorNames( " nameMatchMethodPointcutAdvisor" ); //Specify the Bean that needs to be intercepted proxyFactoryBean.setTarget(testManagerBean()); return proxyFactoryBean; } } Copy 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 called object is:" +proxyFactoryBean.getClass()); proxyFactoryBean.add( 1 , 2 ); proxyFactoryBean.subtraction( 1 , 2 ); } } Copy code

Results of the:

Parameter calculation result: 3 Pre-interception-execution object: xuexi.aop.test.TestManager@a9cd3b1; execution method: subtraction; input parameters: [1, 2] Parameter calculation result: -1 Copy code

From the results, we can see that the method add has not been enhanced; instead, the subtraction method has been enhanced;

There are many types of Advisor, roughly as follows:

  • RegexpMethodPointcutAdvisor: match by regular expression;
  • NameMatchMethodPointcutAdvisor: match by method name;
  • DefaultBeanFactoryPointcutAdvisor: Advisor based on XML configuration, for example: aop:before;
  • InstantiationModelAwarePointcutAdvisorImpl: The advisor for annotation analysis, for example: @Before, @After, etc.;

Why did I say that this Advisor is the focus before? Because in the current development, Spring will encapsulate the notification methods of @Before, @After and other annotations into a corresponding Advisor, a unified Advisor object: InstantiationModelAwarePointcutAdvisorImpl; there is an AspectJExpressionPointcut object in this object , The pointcut is resolved by this object; in the above example, we manually specify a NameMatchMethodPointcutAdvisor, then when a method or class that meets the conditions is encountered during the Spring scanning process, it will be encapsulated into a different Advisor according to the situation;

At this point, I have talked a lot, but it has not completely solved the urgency of creating a ProxyFactoryBean for each class; then how to solve this problem? This is also a requirement for enterprise-level application development, and Spring will of course not ignore it; at the same time, a concept of AutoProxyCreator is introduced in Spring; this AutoProxyCreator uses the concept of Bean's post processor (BeanPostProcessor);

PS: The concept of Bean's post processor doesn't have to be forced by me anymore. If you don't understand, check out my previous article;

Now first add an interface (personal habit, if multiple classes have a unified processing, there will be a unified interface):

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

Modify the test business class:

public class TestManager implements ITestManager { /** * Function description: test method * @date : 2021/4/20 0020 9:23 PM */ @Override public void add ( int a, int b) { System.out.println( "Parameter calculation result:" + (a+b)); } /** * Function description: test method * @date : 2021/4/20 0020 9:23 PM */ @Override public void subtraction ( int a, int b) { System.out.println( "Parameter calculation result:" + (ab)); } } Copy code

Create another test business class to see the effect

public class TestManagerTwo implements ITestManager { /** * Function description: test method * @date : 2021/4/20 0020 9:23 PM */ @Override public void add ( int a, int b) { System.out.println( "Parameter calculation result:" + ((a+b)* 10 )); } /** * Function description: test method * @date : 2021/4/20 0020 9:23 PM */ @Override public void subtraction ( int a, int b) { System.out.println( "Parameter calculation result:" + ((ab)* 10 )); } } Copy code

Modify the configuration class

public class AopConfig { /** * Function description: Inject the test object * * @date : 2021/4/20 0020 8:54 PM */ @Bean public ITestManager testManager () { return new TestManager(); } /** * Function description: Inject the test object * * @date : 2021/4/20 0020 8:54 PM */ @Bean public ITestManager testManagerTwo () { return new TestManagerTwo(); } /** * Function description: Inject the preprocessor * * @date : 2021/4/20 0020 8:54 PM */ @Bean public TestBeforeAdvice testBeforeAdvice () { return new TestBeforeAdvice(); } /** * Function description: Inject surround notification * * @date : 2021/4/20 0020 8:59 PM */ @Bean public TestInterceptor testInterceptor () { return new TestInterceptor(); } @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator () { final BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); //All Bean names that need to create a proxy, use the wildcard here beanNameAutoProxyCreator.setBeanNames( "testManager*" ); //Set the interception enhancer, pay attention to adding here The enhancer is in order, beanNameAutoProxyCreator.setInterceptorNames( "testBeforeAdvice" , "testInterceptor" ); return beanNameAutoProxyCreator; } } Copy code

Finally modify the 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 called object is:" +testManager.getClass()); testManager.add( 1 , 2 ); testManager.subtraction( 1 , 2 ); testManager = (ITestManager) annotationConfigApplicationContext.getBean( "testManagerTwo" ); System.out.println( "The called object is:" +testManager.getClass()); testManager.add( 1 , 2 ); testManager.subtraction( 1 , 2 ); } } Copy code

Results of the

Pre-interception-execution object: xuexi.aop.test.TestManager@5b0abc94; execution method: add; input parameters: [1, 2] Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Parameter calculation result: 3 Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Pre-interception-execution object: xuexi.aop.test.TestManager@5b0abc94; execution method: subtraction; input parameters: [1, 2] Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Parameter calculation result: -1 Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] The called object is: class com.sun.proxy.$Proxy8 Pre-interception-execution object: xuexi.aop.test.TestManagerTwo@75c072cb; execution method: add; input parameters: [1, 2] Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Parameter calculation result: 30 Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Pre-interception-execution object: xuexi.aop.test.TestManagerTwo@75c072cb; execution method: subtraction; input parameters: [1, 2] Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Parameter calculation result: -10 Surrounding interception-execution object: class xuexi.aop.test.TestInterceptor parameters: [1, 2] Copy code

See if all have been represented;

5. the realization principle of AOP annotation method

5.1) Basic process

Looking back at a bunch of examples above, do you feel that it is very close to the effect you are using now? Of course, there are still some differences. Now the full annotation method is used. As mentioned above, different Advisors will be generated according to the annotations when the IOC container is loaded. The approximate processing flow in Spring is as follows:

Here is a rebate for the content of the previous topic "BeanPostProcessor"; so in Spring, IOC is the core and the cornerstone, and everything else is done through open extension points; that is, it is highly 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 the BeanPostProcessor, I can t find the injection port. I have been rubbing my soul for several times. I want to smash the keyboard; I accidentally found a file, opened up a new emperor, and is still continuing to study in depth;

Note: the spring-autoconfigure-metadata.properties file in the Spring source code (find it yourself). This file is the configuration file used by Spring for automatic assembly. There is a paragraph in this file:

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration.ConditionalOnClass=org.aspectj.lang.annotation.Aspect,org.aspectj.lang.reflect.Advice,org.aspectj.weaver.AnnotatedElement,org.springframework.context.annotation. EnableAspectJAutoProxy

Here is a long-winded look: Start annotation: @SpringBootApplication >Annotation to start auto-assembly: @EnableAutoConfiguration >Auto-assembly import processing class: AutoConfigurationImportSelector >Method: selectImports

Note the last EnableAspectJAutoProxy, which is an AspectJAutoProxyRegistrar class introduced in @Import in the annotation;

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 name in the container as: * AnnotationAwareAspectJAutoProxyCreator object of org.springframework.aop.config.internalAutoProxyCreator * You can see after follow up; * This is a post processor object, which is specifically used to parse classes decorated with @Aspectj annotations. Because this object implements the InstantiationAwareBeanPostProcessor interface, * So this post processor is called first in the createBean method of the creation class; * At the same time, this object also implements the BeanPostProcessor interface, so it is still 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 code

This class inherits ImportBeanDefinitionRegistrar, indicating that it is a Bean definition registrar with registration function; an AnnotationAwareAspectJAutoProxyCreator object is registered in the Spring container, let s take a look at the class structure diagram of this diaosi

This AnnotationAwareAspectJAutoProxyCreator is very against the sky, and it also implements three very important interfaces: InstantiationAwareBeanPostProcessor, SmartInstantiationAwareBeanPostProcessor, and BeanPostProcessor;

When entering the Bean creation method createBean, the post processor of the InstantiationAwareBeanPostProcessor type will be called first ( note: here again, it is emphasized that the post processor will be called 9 times during the entire execution of the createBean method ), that is, at this time Start processing the classes annotated with @Aspectj, combined with what was mentioned before, is that when this call is made, the @Before, @After and other annotations in the class that meet the conditions will be packaged into an Advisor object;

SmartInstantiationAwareBeanPostProcessor This type of post processor is specifically used to deal with circular dependencies, so there is no need to explain it; BeanPostProcessor is a post processor interface called after initialization, and it starts to judge whether the current class satisfies the proxy when it is also called in one step. The enhanced conditions, if met, start to create a dynamic proxy class;

5.3) First call

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

The source code of this org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation method has been explained in detail in the previous chapters, here is only the key point

Pay special attention to the shouldSkip method. Normally, this method directly returns false; but it is not so simple in combination with the AnnotationAwareAspectJAutoProxyCreator class, because the shouldSkip method is rewritten in this class, and the correction is completed here. @Aspectj's object has been parsed; let s take a look at the shouldSkip method in AnnotationAwareAspectJAutoProxyCreator (org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip)

@Override protected boolean shouldSkip (Class<?> beanClass, String beanName) { /** * Find the candidate Advisors (notifiers can also be said to be enhancer objects) */ 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 code
@Override protected List<Advisor> findCandidateAdvisors () { //Find the advisors related to the transaction List<Advisor> advisors = super .findCandidateAdvisors(); //Find the aspect- related information and encapsulate it as an advisor if ( this .aspectJAdvisorsBuilder! = null ) { //Get all the aspect cache objects in the container advisors.addAll( this .aspectJAdvisorsBuilder.buildAspectJAdvisors()); } //Return all our notifications return advisors; } Copy code

The buildAspectJAdvisors method is the core of the processing. Here is just a list. The call chain is abnormally deep. Friends who want to challenge the urine world record can follow up;

/** * Go to the container to get all the aspect information and save it in the cache * @return the list of { @link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> buildAspectJAdvisors () { /** * Used to save the name of the aspect, where aspectNames is our class-level cache, and the user caches the parsed aspect information */ List<String> aspectNames = this .aspectBeanNames; //The cache field aspectNames has no value, which means that when the first single-instance bean is instantiated, the parsing aspect operation will be triggered if (aspectNames == null ) { //do dcl Check synchronized ( this ) { aspectNames = this .aspectBeanNames; if (aspectNames == null ) { //Used to save all parsed Advisors collection objects List<Advisor> advisors = new ArrayList<>(); //Used to save the collection of aspect names aspectNames = new ArrayList<>(); /** * In the aop function, the Object object is passed in here, which means to get the names of all the components in the container, and then traverse one by one *, This process is very performance consuming, so spring will add a cache for saving aspect information here. * Note: The transaction function is different. The function of the transaction module is to get the Advisor type directly from the container. The selection range is small and does not consume performance. and so * Spring does not add a cache in the transaction module to save our transaction-related advisors */ String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this .beanFactory, Object.class, true , false ); //traverse the names of all the beans we get from the IOC container for (String beanName: beanNames) { if (!isEligibleBean(beanName)) { continue ; } //Get the corresponding class object from the container through beanName Class<?> beanType = this .beanFactory.getType(beanName); if (beanType == null ) { continue ; } //Judging whether it is an aspect based on the class object if ( this .advisorFactory.isAspect(beanType)) { /* * Add to the cache; * Pay special attention here to exclude the proxy objects generated by AspectJ in isAspect, because the proxy objects generated by AspectJ all start with ajc$ * */ aspectNames.add(beanName); //Build the beanName and class objects into an AspectMetadata AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { //Build an instance factory for aspect annotations MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory( this .beanFactory, beanName); /* * Really get our instance factory * In getAdvisors, you will get all the methods in the aspect class that are modified by @Before, @After and other annotations. * And parse it into an Advisor type packaging class * */ 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; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } /** * When actually creating the aspect, we don t need to parse it, but go directly to the cache to get it. */ 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); //The core method of parsing each annotation getAdvisors advisors.addAll( this .advisorFactory.getAdvisors(factory)); } } return advisors; } Copy code

PS: Before here, a buddy asked me so many annotations, is there an order in their analysis? What kind of order is it? I was really not sure at the time. Later, after seeing the source code here, I discovered that it was really sequential. The specific description is in: org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#ASPECTJ_ANNOTATION_CLASSES. This attribute is based on this attribute. The order of processing starts at once;

private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; Copy code

OK! Go here first;