1, an overview of the

Aop is interface-oriented, that is, method-oriented, and implemented on the basis of IOC. Aop can intercept and enhance specified methods without intruding into business code, separating business from non-business processing logic, such as Spring transactions, through transaction annotation configuration, Spring will automatically start and commit the business in the business method, and execute the corresponding rollback strategy if the business process fails. Aop implementation consists of two main parts:

  • Match qualified methods (Pointcut)

  • Method enhancements for matching (JDK proxy cglib proxy)

Spring for XML configuration and configuration of automatic proxy Advisor has a big difference, in IOC is mainly based on XML configuration analysis, in AOP source code interpretation, it is mainly from the automatic proxy way of parsing, analysis of annotations, then analysis of XML-based way.

2. Case preparation

The following is an example of how Spring AOP is used for source code analysis

Aspect class: TracesRecordAdvisor

@Aspect
@Component
public class TracesRecordAdvisor {
    
    @Pointcut("execution(* spring.action.expend.aop.services.*.*(..) )")
    public void expression(a) {}@Before("expression()")
    public void beforePrint(a)
    {
        System.out.println("Enter service, log start....");
    }

    @AfterReturning("expression()")
    public void afterPrint(a)
    {
        System.out.println("Exit service, log out....."); }}Copy the code

XML configuration: AOP annotation enablement requires only this code to be configured in XML, which is used as an entry point

    <aop:aspectj-autoproxy/>
Copy the code

Service class: PayServiceImpl uses a JDK proxy so it has an interface

@Service
public class PayServiceImpl implements PayService {
    public void payMoneyService() {
        System.out.println("Payment service in progress..."); }}Copy the code

Test method:

   @Test
    public void springAopTestService(a) {
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-aop.xml");
        PayService payService= (PayService) applicationContext.getBean("payServiceImpl");
        payService.payMoneyService();
    }
Copy the code

Execution Result:

Enter the service and start logging.... Payment service is in progress... Log out of the service and exit.....Copy the code

From the above results, the payMoneyService method is indeed enhanced.

3, spring BeanFactoryPostProcessor

While reading the Spring source code, I want to start by looking at BeanFactoryPostProcessor and BeanPostProcess, both of which allow us to generate BeanDefinitions after Spring has generated beanDefinitions by retrieving bean declarations from configuration files or XML Entry for repackaging.

Let’s start with the definition of BeanFactoryPostProcessor

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory); }Copy the code

Methods postProcessBeanFactory parameters for ConfigurableListableBeanFactory, remember the beanFactory used to retrieve the bean, And interface SingletonBeanRegistry and BeanFactroy ConfigurableListableBeanFactory inheritance, so can access to have generated BeanDefinitions collection, if a class implements the interface, Spring will register this class and then execute the postProcessBeanFactory method of this class so that we can extend the BeanDefinition.

I’ll give you a brief introduction to BeanFactoryPostProcessor, just to show that in Spring, we can modify the generated BeanDefinition, Take a look at how Spring registers BeanFactoryPostProcessor and executes postProcessBeanFactory.

	@Override
	public void refresh(a) throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			prepareRefresh();
             // Core method 1
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			prepareBeanFactory(beanFactory);
			try {
				postProcessBeanFactory(beanFactory);
                 // Core method 2 executes BeanFactoryPostProcessor
				invokeBeanFactoryPostProcessors(beanFactory);
				// Core method 3 registers BeanPostProcessor
				registerBeanPostProcessors(beanFactory);
				// Initialize message source for this context.
				initMessageSource();
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				// Check for listener beans and register them.
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				finishRefresh();
            }
			catch (BeansException ex) {
                 ............
				throw ex;
			}
			finally{... resetCommonCaches(); }}}Copy the code

The core method 1obtainFreshBeanFactory is the entry point for generating beanDefinitions as described in the previous two articles, InvokeBeanFactoryPostProcessors core method 2 is spring BeanFactoryPostProcessor interface method of execution.

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	}
Copy the code

By method get registered spring BeanFactoryPostProcessor getBeanFactoryPostProcessors, and then look at how to add a processor

	@Override
	public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
		this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
	}
Copy the code

Method for invokeBeanFactoryPostProcessors stopped to look down, the inside of the method of spring BeanFactoryPostProcessor roughly sorted, sorting standard is whether realized PriorityOrdered, Then according to the size of the order of Settings specified execution order, and generate a sorting collection and a regular collection, finally invokeBeanFactoryPostProcessors execution

	private static void invokeBeanFactoryPostProcessors( Collection
        postProcessors, ConfigurableListableBeanFactory beanFactory) {

		for (BeanFactoryPostProcessor postProcessor : postProcessors) {
		     // Executes to a custom BeanFactoryPostProcessorpostProcessor.postProcessBeanFactory(beanFactory); }}Copy the code

This method loops through the previously registered BeanFactoryPostProcessor collection and then executes postProcessBeanFactory.

4, BeanPostProcess

BeanPostProcess is much more important than BeanFactoryPostProcessor because Spring annotations, AOP, and so on are executed through this interface method. It is implemented throughout the lifecycle of the Bean creation process. In the IOC phase, Spring only registers the BeanPostProcess, and the execution is put into the Bean’s instantiation creation phase.

First take a look at the BeanPostProcessor interface definition

public interface BeanPostProcessor {
    // Executes after the Aware interface executes after the bean creates the attribute assignment
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    // execute after init-method afterPropertiesSet
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

Copy the code

In the bean declaration cycle, the following sequence is the order of interfaces and methods to be executed after the bean is created:

Instantiation (autowireConstructor or instantiateBean) — — — — — — — — — — — — — — — attribute initialization (populateBean) — — — — — — — — — — — — — Aware

(if the bean implementation) — — — — — — — — — — — — — — — — — — — — — — — — — – BeanPostProcess. PostProcessBeforeInitialization — — — — — — — — — — — — — — — — — —

PostConstructInitializingBean.afterPropertiesSet—–BeanPostProcess.postProcessAfterInitialization

Which is by means of introduce annotations rely on in AutowiredAnnotationBeanPostProcessor implemented in this class, and then to analyze Spring Aop is also start from here, This class is called AnnotationAwareAspectJAutoProxyCreator,

5, NameSpaceHanlder

In Spring, everything is built on IOC, and Aop is no exception. The program first reads the XML configuration file, then searches the namespace for the read tag, then finds the corresponding NameSpaceHandler, and finally calls the parse method to parse the tag.

Aop: AspectJ-AutoProxy is not quite the same as aop: Config when generating PointCut and Before, After, and Around classes. Using aop:config generates a BeanDefinition for these annotations, and the constructor of this BeanDefinition is composed of three BeanDefinitions, indicating that the class is a synthetic class, i.e. synthetic is true. These instance objects are then generated in the same way that normal beans are parsed. The following analysis is based on pure annotation analysis, that is, parsing starts with parsing the AOP: AspectJ-AutoProxy tag.


< AOP: Aspectj-autoProxy /> < AOP: Aspectj-autoProxy /> < AOP: Aspectj-autoProxy /> < AOP: Aspectj-autoProxy /> ParseCustomElement (root); parseCustomElement(root);

  • Get Element’s nameSpaceUri and find the NameSpaceHanlder based on nameSpaceUri

  • Call the Parse method of NameSpaceHanlder to parse element

The following is the definition of the NameSpaceHanlder interface

public interface NamespaceHandler {
	void init(a);
	BeanDefinition parse(Element element, ParserContext parserContext);
	BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}

Copy the code

The init method is used to initialize the tag, set the parser for the tag, find the tag in the parse method, and call the parse method to parse the tag. We will focus on these two methods later.

The handlers file is located in the meta-INF/directory in the project space. The handlers file is under the Jar that Spring depends on. Handlers and Handlers are stored in this file in the core JAR package path of aop: Spring

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
Copy the code

Aop nameSpaceUri (key); aop NamespaceHandler (value); aop NamespaceHandler (AopNamespaceHandler) The init and parse methods must also be implemented, so parsing < AOP: Aspectj-AutoProxy /> is handled by AopNamespaceHandler’s parse.

Look at the init method of AopNamespaceHandler

@Override
	public void init(a) {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config".new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy".new                       AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy".new ScopedProxyBeanDefinitionDecorator());
		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured".new SpringConfiguredBeanDefinitionParser());
	}
Copy the code

The code above is very clear, < aop: config > tag by ConfigBeanDefinitionParser processing, < aop: aspectj autoproxy / > is handled by AspectJAutoProxyBeanDefinitionParser this class, these two kinds of processing actually corresponds to the automatic agent and in the processing of XML configuration way, Then call AspectJAutoProxyBeanDefinitionParser parse method

	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}
Copy the code

This method is to register a AnnotationAwareAspectJAutoProxyCreator class, All of AOP’s processing logic is then handed over to this class. Since this class implements BeanPostProcessor, its entry point is the two methods of the BeanPostProcessor interface:

  • postProcessBeforeInitialization
  • postProcessAfterInitialization

6, Spring Aop source code interpretation prelude

< AOP: aspectj-AutoProxy /> < AOP: aspectj-AutoProxy /> < AOP: aspectJ-AutoProxy /> This class then entrust this tag AspectJAutoProxyBeanDefinitionParser class, finally call this class to parse method, do not make the parse method, in fact, the purpose of this method is very simple, Is a registered AnnotationAwareAspectJAutoProxyCreator this class, this class implements the BeanPostProcessor and InstantiationAwareBeanPostProcessor interface, Finally, the methods of both interfaces are called (in the following order) during the instantiation of the bean object, which is beanFactory.getBean (beanName) :

InstantiationAwareBeanPostProcessor perform first:

postProcessBeforeInstantiation(Class<? > beanClass, String beanName)

postProcessAfterInstantiation(Object bean, String beanName)

BeanPostProcessor:

postProcessBeforeInitialization(Object bean, String beanName)

Object postProcessAfterInitialization(Object bean, String beanName)

AOP implementations are basically implemented in these two methods, so here’s how Spring implements AOP. Spring’s AOP proxy currently supports method enhancements, and the source code currently seems to support attribute enhancements.

Before reading the source code, first analyze the principle of method enhancement, which helps us to grasp the main line when reading the source code. First of all, if we want to enhance the methods of a class, what should we do?

This business requirement can be implemented by proxy, intercepting the method before it is executed, adding logic to enforce it, and finally executing the target method. Here are two proxy approaches used by Spring:

JDK Proxy: We can use the Proxy class to get a Proxy object for a target class, but the JDK Proxy requires that the propped class implement an interface, so it is an interface-based Proxy.

Cglib proxy: If the target class does not have an interface, use the Cglib proxy, which is encapsulated by ASM, directly manipulate the class to obtain bytecode, which is also very efficient.

Since it is not possible to enhance all classes in a production business, we also need a selector to enhance qualified beans. Spring uses the PointCut interface to get a method matcher through the getMethodMatcher method of that interface. The enhancement is then performed using the Matches method to match the target method of the target class object. Mathcer matching rules are expressions configured through Spring.

So when analyzing the source code, we should focus on these two aspects:

  • Match pointcut methods (build pointcut expression classes and pointcut classes)

  • Creating a proxy object

These two aspects can be complicated in Spring implementations, especially in the first step of matching the Pointcut method. In this process, Spring wraps the @before, @After, @around, and @pointcut annotations of the @aspect annotation class into the Aspect method class to be executed. Then execute the section method class before and after the method to be enhanced matched by the method matcher to achieve the purpose of method enhancement.

In the second stage, the proxy object is created by default through the JDK proxy implementation configuration, < AOP :aspectj-autoproxy proxy-target-class=”true”> this configuration can specify the use of cglib proxy.

Annotation section proxy class

The above analysis of the real realization of AOP functionality is AnnotationAwareAspectJAutoProxyCreator, as a result of this class implements the BeanPostProcessor and InstantiationAwareBeanPostProcessor So when creating a bean, you will enter the methods of the two interfaces. The two interfaces contain four methods. The order of execution of the methods has been analyzed above.

< AOP: Aspectj-autoproxy /> < AOP :aspectj-autoproxy/> < AOP :aspectj-autoproxy/> < AOP :aspectj-autoproxy/> < AOP :aspectj-autoproxy/> < AOP :aspectj-autoproxy/> < AOP :aspectj-autoproxy/> < AOP :aspectj-autoproxy/> This class is registered with the container, and the order in which the four methods of the two interfaces are called is listed above. The two methods in this class perform the main functions and their execution order:

InstantiationAwareBeanPostProcessor perform first:

postProcessBeforeInstantiation(Class<? > beanClass, String beanName)

BeanPostProcessor:

Object postProcessAfterInitialization(Object bean, String beanName)

PostProcessBeforeInstantiation method is mainly to find annotations to the Advice of class, and will use the @ Before Advice to the class, @ After, Annotation methods such as @around, @pointcut, and @afterthrowing are wrapped into a cache class by class to generate proxies for matched classes. PostProcessAfterInitialization mainly matching eligible target class object, then the process of generating agent, then in order to analyze the two methods function.

8. Handle Aspect annotation classes

	public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
        // Build a cache key
		Object cacheKey = getCacheKey(beanClass, beanName);
		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
             // If the current beanClass cache key exists in the cache with Class Advise, the current beanClass is Adivse
             // And no proxy generation is required.
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
             // Core check: 1 whether the current class is AOP base class 2, whether the current class should skip not generate proxy
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null; }}// This section is mainly used for the bean that implements the TargetSource interface and then gets the object from getTarget to create the proxy
		if(beanName ! =null) {
			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
			if(targetSource ! =null) {
				this.targetSourcedBeans.add(beanName);
				Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
				Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
				this.proxyTypes.put(cacheKey, proxy.getClass());
				returnproxy; }}return null;
	}
Copy the code

This method generates a cached key for the beanClass. If the beanClass is a FactoryBean, it is named according to the factory class naming rules. Otherwise, it is named beanName. Then use the generated key to determine if the beanClass already exists in the Advice cache. If it does, the class is faceted and has already been processed. No proxy is generated for the class. The advice cache is ignored when proxying beans. The advice cache is ignored when proxying beans. The advice cache is ignored when proxying beans.

AnnotationAwareAspectJAutoProxyCreator rewrite the method

@Override
protected boolean isInfrastructureClass(Class
        beanClass) {
// Call the parent's isInfrastructureClass to determine if it is an AOP base class
// Verify that the current class uses @aspect annotations
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
}
Copy the code

IsInfrastructureClass method of the parent class

	protected boolean isInfrastructureClass(Class
        beanClass) {
		boolean retVal = Advice.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

IsAssignableFrom (); isAssignableFrom (); beanClass (); Advice (); That is, whether the class is Advice,Advisor, or implements the AopInfrastructureBean interface. This method calls the parent’s isInfrastructureClass to determine whether it is an AOP base class, and then verifies that the current class uses the @aspect annotation.

And then shouldSkip(beanClass, beanName) :

	@Override
	protected boolean shouldSkip(Class
        beanClass, String beanName) {
		// Look for all Advisor section classes that have been generated
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			if (advisor instanceof AspectJPointcutAdvisor) {
				if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
					return true; }}}return super.shouldSkip(beanClass, beanName);
	}
Copy the code

This method checks whether the beanName being created is already in the section class cache. If so, it is added to the advices cache and no further processing is needed. FindCandidateAdvisors () will find all classes generated in the current container that implement the Advisor, and Spring will replace @before, @after, @around and so on generate an inherited Advisor class object and store it in the cache for subsequent use. This part is the core content of the first half of Spring AOP, and the subsequent exploration will focus on how to generate the annotations of the tangent class into the Adisor class.

AnnotationAwareAspectJAutoProxyCreator rewrite the findCandidateAdvisors method, so will perform to the method:

@Override
protected List<Advisor> findCandidateAdvisors(a) {
   // Find the Advisor classes in all containers through the parent class method, which is generated by < AOP :before/> based on the XML configuration
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Find the Advisor class generated by annotations
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}
Copy the code

This method will first call the parent’s findCandidateAdvisors method to get the Advisor generated by the CONFIGURATION of an XML file, i.e. < AOP :before>,< AOP :after>, etc., and then call @before via annotations, @After, @around, @pointcut, and @afterthrowing generate advisors that handle the XML-based configuration file and annotation-based configuration, respectively. Because all the analysis is carried out based on AnnotationAwareAspectJAutoProxyCreator this class, so in this place will first retrieve configuration files, generating annotation-based class Advisor, so that it will be based on the XML configuration and annotation-based configuration will be parsing.

Look at this. AspectJAdvisorsBuilder. BuildAspectJAdvisors ()

	public List<Advisor> buildAspectJAdvisors(a) {
		List<String> aspectNames = null;
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new LinkedList<Advisor>();
				aspectNames = new LinkedList<String>();
                // Get all beanName definitions from beanDefinitions
				String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true.false);
				for (String beanName : beanNames) {
                      // If beanName does not match the configured 
      
                      // Ignore all aspect methods on this bean
					if(! isEligibleBean(beanName)) {continue; } Class<? > beanType =this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
                      // If the current beanType is an aspect class, encapsulate the information about that aspect class
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {                   
/ / the plane information into the partial shipments to generate a AspectMetadata MetadataAwareAspectInstanceFactory
MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// Getting all Advisor classes in the container requires a detailed analysis of this method
List<Advisor> classAdvisors=this.advisorFactory.getAdvisors(factory);
							if (this.beanFactory.isSingleton(beanName)) {
                                   // Add the singleton to the cache
								this.advisorsCache.put(beanName, classAdvisors);
							}
							else {
                                   // Add factories to the cache without singletons
								this.aspectFactoryCache.put(beanName, factory);
                            }
							advisors.addAll(classAdvisors);
						}
						else {
							// Add the factory class that generated the Advisor to the cache
							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; }}... }Copy the code

The main task of this method is to obtain the Aspect class of the class type, and then obtain all the annotations of the Aspect class method and convert the annotations into the Advisor class return. The main steps are as follows:

  • Gets the beanName of all BeanDefinitions in the container

  • Matches the Aspect Aspect classes that match the rules based on the beanName, or beanClass, configured via < AOP :include>

  • All Aspect methods that get the Aspect Aspect class are returned wrapped as Advisor objects.

  • Put all the advisors that you get into the cache.

The method code, though many, but the core is enclosing advisorFactory. GetAdvisors (factory), namely the third step, this method will get to the plane class all the aspects of method, and encapsulated into Advisor, getAdvisors is an interface, ReflectiveAspectJAdvisorFactory implements this interface, the code below is its implementation logic:

	@Override
	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
         // Get the section ClassClass<? > aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();// Get the beanName of the section class
		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
		validate(aspectClass);
	    // Further encapsulation of AspectMetadata contains information about the aspect class
		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
		List<Advisor> advisors = new LinkedList<Advisor>();
         // Get methods in the aspect class that do not use the Pointcut annotation
		for (Method method : getAdvisorMethods(aspectClass)) {
             // Check if this method is a section method, if it is an Advisor class return
			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
			if(advisor ! =null) { advisors.add(advisor); }}// Set an empty if there is no section method
		if(! advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor =new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
			advisors.add(0, instantiationAdvisor);
		}
		Spring supports enhancements to properties
		for (Field field : aspectClass.getDeclaredFields()) {
			Advisor advisor = getDeclareParentsAdvisor(field);
			if(advisor ! =null) { advisors.add(advisor); }}return advisors;
	}
Copy the code

This method first has to cut the class information encapsulation to AspectMetadata class encapsulation to MetadataAwareAspectInstanceFactory again, and then get cut class all did not use the Pointcut annotation methods, Call getAdvisor to get the slice annotation used by this method and generate the corresponding Advisor class. The PointCut processing is handled later in the getAdvisor.

9. Get the Advisor for the section class

GetAdvisor = getAdvisor ();

// The section method of the section class may be beforePrint()
Method  candidateAdviceMethod 
// Get the instance factory of AspectMetadata (you can get all the information about the class in the aspect)
MetadataAwareAspectInstanceFactory aspectInstanceFactory
// Sort the section
int declarationOrderInAspect
// beanName of the section class
 String aspectName
Copy the code

The section class and the section method are retrieved from the above parameters to obtain an Advisor object, and a PointCut expression, PointCut, is needed to match the qualified methods. Once the target method is intercepted, the Adivsor enhanced method can be executed. Taking a look at the process of creating the Advisor, we assume that Method is the beforePrint Method of the TracesRecordAdvisor class, which in our test case creates the aspect Method with the @before annotation:

@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {

   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
   // Get the pointCut, where we actually get expression(), which corresponds to the content of the pointCut
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
    / / create the advisor
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
Copy the code

To see how the getPointCut method fetches exression, there are many steps that need to be nested. Instead of expanding it here, let’s look at how to set the values found in the expression:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class
        candidateAspectClass) {
   //AspectJAnnotation<? > aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {
      return null;
   }

   AspectJExpressionPointcut ajexp =
         new AspectJExpressionPointcut(candidateAspectClass, new String[0].newClass<? > [0]);
   // Put the expression method parsed by the AspectJAnnotation notation generated above into the expression
   //
   ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
   return ajexp;
}
Copy the code

Here need to pay attention to the above findAspectJAnnotationOnMethod method:

protected staticAspectJAnnotation<? > findAspectJAnnotationOnMethod(Method method) {// See the familiar section method annotation, where beforePrint uses the @before annotationClass<? >[] classesToLookFor =newClass<? >[] { Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};for(Class<? > c : classesToLookFor) { AspectJAnnotation<? > foundAnnotation = findAnnotation(method, (Class<Annotation>) c);if(foundAnnotation ! =null) {
         returnfoundAnnotation; }}return null;
}
Copy the code

This method looks for Before, Around, After, AfterReturning, AfterThrowing, and Pointcut annotations for the section method, and returns an AspectJAnnotation object if it does. The generic object that contains an annotation is the one that is set to the value of the annotation, and it also gets the content of the pointcut expression that is configured in the annotation, and if the expression method is referenced, the method parameter is set to the pointcutExpression property.

Having parsed the annotations for the aspect method, now go back to how to create an instance of the Advisor:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
      Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
   this.declaredPointcut = declaredPointcut;
   this.aspectJAdviceMethod = aspectJAdviceMethod;
   this.aspectJAdvisorFactory = aspectJAdvisorFactory;
   this.aspectInstanceFactory = aspectInstanceFactory;
   this.declarationOrder = declarationOrder;
   this.aspectName = aspectName;
    // Whether the section class is lazily loaded
   if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      // Static part of the pointcut is a lazy type.
      Pointcut preInstantiationPointcut = Pointcuts.union(
            aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

      // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
      // If it's not a dynamic pointcut, it may be optimized out
      // by the Spring AOP infrastructure after the first evaluation.
      this.pointcut = new PerTargetInstantiationModelPointcut(
            this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
      this.lazy = true;
   }
   else {
      this.pointcut = this.declaredPointcut;
      this.lazy = false;
       // This will eventually get an advice
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); }}Copy the code

10. Create Advice for the aspect method

InstantiateAdvice (this.declaredPointcut) : instantiateAdvice(this.declaredPointcut)

@Override
	public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
         // Get the aspect class object, in this case TracesRecordAdvisorClass<? > candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass);// Core point 1: Get the section annotation by using the @before annotation Before PrintAspectJAnnotation<? > aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {
			return null; }... AbstractAspectJAdvice springAdvice;// Core point 2: Generate different Advice classes based on the annotations transformed.
		switch (aspectJAnnotation.getAnnotationType()) {
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				}
				break;
			case AtAfterThrowing:
				springAdvice = new AspectJAfterThrowingAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
				}
				break;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtPointcut:
                // PointCut is not handled here
				if (logger.isDebugEnabled()) {
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				}
				return null;
			default:
				throw new UnsupportedOperationException(
						"Unsupported advice type on method: " + candidateAdviceMethod);
		}

		// Configure the aspect class information into SpringAdvice
		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

We’ve already looked at core point 1, but the above method is only used to get exression expressions on annotations. Here we call the Advice class for annotations to get the mapping classes for the tangent annotations and AspectJAnnotation.

protected staticAspectJAnnotation<? > findAspectJAnnotationOnMethod(Method method) {// See the familiar section method annotation, where beforePrint uses the @before annotationClass<? >[] classesToLookFor =newClass<? >[] { Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};for(Class<? > c : classesToLookFor) { AspectJAnnotation<? > foundAnnotation = findAnnotation(method, (Class<Annotation>) c);if(foundAnnotation ! =null) {
         returnfoundAnnotation; }}return null;
}
Copy the code

This method checks whether the section method implements the Before, Around, After, AfterReturning, AfterThrowing, and Pointcut annotations. If so, it returns an AspectJAnnotation object. There’s a generic object with an annotation, and that generic object is the value that’s being set to those annotations. Eventually these objects are converted into the following objects stored in the AspectJAnnotation:

static {
   // The class that converts the annotation to the AspectJAnnotationType enumeration that follows.
   annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
   annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
   annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
   annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
   annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
   annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
}
Copy the code

With core point 1, Spring has converted the annotation @before correspondence to AtBefore, @after to AtAfter, and so on, all mapped to the switch condition class at core point 2, where Advice classes are generated for the corresponding section annotation class. All annotation aspect class concrete implementations are implemented by AbstractAspectJAdvice, an abstract class whose constructor takes three arguments:

// The section method here may be beforePrint
Method aspectJAroundAdviceMethod
// Pointcut expression matcher refers to the matcher that encapsulates Exression
AspectJExpressionPointcut pointcut
TracesRecordAdvisor TracesRecordAdvisor
AspectInstanceFactory aif
Copy the code

Here is the Advice class Spring generates for the corresponding annotation

Annotation class Advice Method
AtBefore AspectJMethodBeforeAdvice
AtAfter AspectJAfterAdvice
AtAfterReturning AspectJAfterReturningAdvice
AtAfterThrowing AspectJAfterThrowingAdvice
AtAround AspectJAroundAdvice

Each annotation performs its own enhancement methods in a different reality, this part just generates the Advice class, which is then cached and called when the proxy is actually generated. At this point, Spring has converted the @aspect annotated Aspect class’s Aspect methods into the corresponding Adivsor class, which contains the Aspect methods, the encapsulated PointCut filter, and the instance objects that generate the Aspect class. This class matches the target method of the qualified target class and then performs the enhancement.

The Advice class generated by the section annotation is eventually put into a cache. When the target bean is generated, all the Advice that matches the target bean is put into the collection. The invocation is managed by a MethodInvocation class that explains in more detail later. Here’s a quick look at the Invoke method of AspectJAfterAdvice to see how it is invoked

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
             The invocation is a chained invocation class that implements the MethodInvocation method
			return mi.proceed();
		}
		finally {
            // Finally perform the post-enhancement method
			invokeAdviceMethod(getJoinPointMatch(), null.null); }}Copy the code

The invoke method needs a the MethodInvocation parameters, the Advice above class except AspectJMethodBeforeAdvice implements this interface, so can realize chain calls, this logic will explain in detail when creating agent, For a brief analysis, the advice invoke method specifies when the aspect method is to be enhanced.

An AOP proxy peek

The previous part of the operation mainly deals with the @aspect annotation of the Aspect class, and then all the Aspect methods of the Aspect class generate corresponding Advisor based on the annotation used. This Advisor contains the Aspect method, pointcut matcher and Aspect class, that is, the logic to be enhanced is aligned. The next step is to inject the logic in place for enhancement, which is done by the well-worn proxy.

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if(bean ! =null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
       // Enter the following method if the class to be created is not a pre-exposed proxy
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

Before creating a proxy, verify that the bean needs to create a proxy

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // If the bean is obtained through the TargetSource interface, it is returned directly
   if(beanName ! =null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
    // Returns directly if the bean is a faceted class
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
    // If the bean is an Aspect and allows the creation of a proxy to be skipped, add advise to the cache return
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }
   // Create the proxy if there is an Advisor in the previously generated Advisor cache that matches the target class method
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
       // Create the proxy
      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

Method is simple, the main focus on getAdvicesAndAdvisorsForBean and createProxy, first is to obtain the set of method can match the target class Advisor, if this set is not empty, represents the class need to be enhanced, need to generate agent, if you do not match, Indicates that the class does not need to be enhanced and no proxy needs to be created. The createProxy method determines whether to use a JDK proxy or a Cglib proxy, and uses the previously generated Advisor to implement enhancements. This will be discussed in the next article.

12. Summary

To summarize, the main tasks Spring AOP accomplishes in the first phase:

Reading configuration file phase:

  • Reads the XML file with < aop: aspectj autoproxy / > tag, find the namespace handler AopNamespaceHandler, then find the tag class AspectJAutoProxyBeanDefinitionParser processing

  • Through AspectJAutoProxyBeanDefinitionParser parse method, will AspectJAwareAdvisorAutoProxyCreator registration statement cycle to the container.

Bean creation phase:

  • Perform AspectJAwareAdvisorAutoProxyCreator postProcessBeforeInstantiation checking whether the target class is Aspect class base class and AOP and whether the agent does not need to be performed to skip class

  • Get all beanDefinitions classes that use Aspect annotations, and put the Section method into the cache to generate the Advisor class from the annotations used (key)

  • Call AspectJAwareAdvisorAutoProxyCreator postProcessAfterInitialization method, to enhance the class to create a proxy.

This is what Spring AOP does at this stage, and the next section will focus specifically on how Spring implements JDK and Cglib proxy analysis.