Since springAOP will use springIOC to manage beans, those who are not familiar with springIOC can refer to my previous springIOC source code in-depth analysis.

The source code version used in this article is 5.2.x. To better understand springAOP, we use XML, and most of our actual development is in annotations. My experience has taught me that XML configuration is best from a source code understanding perspective.

Reading advice: Pull down the source code of spring’s official website and compare it to my analysis, so that you can learn the most efficiently, rather than skimming.

This article is divided into two parts. The first part introduces some of the pre-knowledge of Spring AOP. The purpose of this part is to let readers who are not familiar with Spring AOP understand some of the basic knowledge of Spring AOP. The second part is the focus of this article, which will provide an in-depth analysis of springAOP source code.

An introduction to spring AOP terminology

  • Joinpoint

    The so-called join points are those points that are intercepted. In Spring, these points refer to methods, because Spring only supports join points of method types

    Corresponding class:

    public interface Joinpoint {
    
      // Executes the next interceptor logic in the interceptor chain
    	Object proceed(a) throws Throwable;
    
    	Object getThis(a);
    
    	AccessibleObject getStaticPart(a);
    
    }
    Copy the code
  • Pointcut

    A pointcut is the definition of which JoinPoints we want to intercept

    Corresponding class:

    public interface Pointcut {
    
        // Return a type filter
        ClassFilter getClassFilter(a);
    
        // Return a method matcher
        MethodMatcher getMethodMatcher(a);
    
        Pointcut TRUE = TruePointcut.INSTANCE;
    }
    Copy the code

    The corresponding ClassFilter and MethodMatcher interfaces:

    public interface ClassFilter {
        boolean matches(Class
              clazz);
        ClassFilter TRUE = TrueClassFilter.INSTANCE;
    
    }
    
    public interface MethodMatcher {
        boolean matches(Method method, Class
              targetClass);
        boolean matches(Method method, Class
              targetClass, Object... args);
        boolean isRuntime(a);
        MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
    }
    Copy the code

    When we write AOP code, usually with breakthrough point expression to select join points, point expression processing categories: AspectJExpressionPointcut, inheritance of this class diagram is as follows:

Can see AspectJExpressionPointcut inherited ClassFilter and MethodMatcher interface, have matches method, so it can choose to join.

  • Advice (notification/enhancement)

    After notification means intercepted Jointpoint have to do is to inform, notice is divided into pre notice (AspectJMethodBeforeAdvice), rear notification (AspectJAfterReturningAdvice), Abnormal notice (AspectJAfterThrowingAdvice), final notice (AspectAfterAdvice), surrounding the notification (AspectJAroundAdvice)

  • The Introduction (Introduction)

    Introduction is a special kind of notification. Without modifying the class code, Introduction can dynamically add methods or fields to a class at run time.

  • Target = Target object

    The target object of the agent, such as the UserServiceImpl class

  • Weaving (Weaving)

    The process of applying an enhancement to a target object to create a new proxy object. Spring implements weaving by implementing the back-end processor BeanPostProcessor interface. That is, after the bean is initialized, the springIOC container takes over by generating a proxy object for the target object, so that the target object retrieved from the container is the enhanced proxy object.

  • Proxy

    When a class is woven into AOP for enhancement, a result proxy class is produced

  • Advisor (notifier, Advisor)

    It’s very similar to Aspect

  • Aspect (Aspect)

    Is a combination of pointcut and advice, corresponding to the PointcutAdvisor in Spring, that allows you to get both pointcuts and advice. As shown in the following class:

    public interface Advisor {
    
        Advice getAdvice(a);
        boolean isPerInstance(a);
    }
    
    public interface PointcutAdvisor extends Advisor {
    
        Pointcut getPointcut(a);
    }
    Copy the code

    For the above knowledge points, through the following figure to sum up, I hope to help readers remember.

Understand the concepts of AOP, Spring AOP, AspectJ

What is the AOP

AOP is the abbreviation of Aspect Oriented Programming, meaning: Aspect Oriented Programming. Please refer to relevant definitions for details

Spring AOP

  • Based on dynamic proxy implementation, if the interface is used, then use JDK dynamic proxy implementation; If no interface is implemented, CGLIB is used.
  • Dynamic proxies are designed based on reflection.
  • Spring provides AspectJ support and implements a pure set of Spring AOP

This article analyzes the source code based on springAOP parsing.

The principle is shown in the figure below:

Maybe it’s a little abstract, but here’s an example to help you understand:

Create a Person interface:

public interface Person {

    public void eat(a);
}
Copy the code

Create the interface implementation class: PersonImpl

public class PersonImpl implements Person {

    @Override
    public void eat(a) {
        System.out.println("I want to eat."); }}Copy the code

When our mothers told us to wash our hands before eating, how do we use code to do that?

Define an EatHandler that implements InvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class EatHandler implements InvocationHandler {

    // Target object to be proxied
    private Object target;

    public EatHandler(Object target) {
        this.target = target;
    }
    public Object proxyInstance(a) {
        // The first argument is the class loader to load the proxy object,
        // The second argument is the interface to be implemented by the proxy class
        // The third parameter is EatHandler
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Wash your hands before eating.");
        Object result = method.invoke(this.target, args);
        System.out.println("Wash the dishes after eating.");
        returnresult; }}Copy the code

Here’s a test class:

public class ProxyMain {
    public static void main(String[] args) {
        EatHandler eatHandler = new EatHandler(newPersonImpl()); Person person = (Person) eatHandler.proxyInstance(); person.eat(); }}Copy the code

Output:

Wash your hands before eating I wash the dishes after eatingCopy the code

As you can see, before and after “I want to eat” we have inserted “wash your hands before eating” and “wash your dishes after eating” functions

How do you insert it?

The idea is that the JDK creates a proxy class for the target object (EatHandler). This proxy class inserts the desired functionality when we call Person.eat (); We operate on the proxy class instead of the original object, thus fulfilling the need to insert the functionality we want. That should make sense. The generated proxy object is posted below:

import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 
// Decomcompiled by Procyon v0.5.36
// 

public final class PersonImpl$Proxy1 extends Proxy implements Person
{
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    public PersonImpl$Proxy1(final InvocationHandler h) {
        super(h);
    }
   
    public final boolean equals(final Object o) {
        try {
            return (boolean)super.h.invoke(this, PersonImpl$Proxy1.m1, new Object[] { o });
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw newUndeclaredThrowableException(undeclaredThrowable); }}public final void eat(a) {
        try {
            super.h.invoke(this, PersonImpl$Proxy1.m3, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw newUndeclaredThrowableException(undeclaredThrowable); }}public final String toString(a) {
        try {
            return (String)super.h.invoke(this, PersonImpl$Proxy1.m2, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw newUndeclaredThrowableException(undeclaredThrowable); }}public final int hashCode(a) {
        try {
            return (int)super.h.invoke(this, PersonImpl$Proxy1.m0, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw newUndeclaredThrowableException(undeclaredThrowable); }}static {
        try {
            PersonImpl$Proxy1.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
          // Use reflection to get the target object Method
            PersonImpl$Proxy1.m3 = Class.forName("Person").getMethod("eat", (Class<? > [])new Class[0]);
            PersonImpl$Proxy1.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<? > [])new Class[0]);
            PersonImpl$Proxy1.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<? > [])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw newNoClassDefFoundError(ex2.getMessage()); }}}Copy the code

We see that the JDK does generate a Proxy object for us that inherits the Proxy object and implements the Person interface. We see equals(), toString(), hashCode(), and our eat() methods generated in the proxy object. Super.h.i nvoke(this, PersonImpl$proxy1.m3, null); Where h is EatHandle as defined. We see that when we call Person.eat (), we are actually calling the eat() method of the proxy object, starting with the EatHandle invoke method, which has the enhanced code we added, and this implements the proxy. So you get the idea.

AspectJ

  • AspectJ is an AOP framework implemented in Java that enables AOP compilation of Java code (typically at compile time) to give it AspectJ’s AOP capabilities (requiring a special compiler, of course)
  • AspectJ is arguably the most mature and feature-rich language currently implementing AOP frameworks. More fortunately, AspectJ is fully compatible with Java programs and is almost seamless, making it easy for engineers with a Java programming background to get started and use.
  • ApectJ uses static weaving. ApectJ mainly uses compile-time weaving, in which AspectJ’s ACJ compiler (similar to JavAC) is used to compile aspect classes into class bytecode and then at Java target class compilation time, that is, compile the aspect classes first and then the target classes.

As shown below:

The source code parsing

With the foundation of the previous knowledge, now can finally enter the source code analysis stage. Let’s test our ideas. Please open the spring source.

This stage is divided into three steps:

  • AOP configuration file parsing process analysis
  • Proxy object creation analysis
  • Proxy objects perform process analysis

AOP configuration file parsing process analysis

We need to finish parsing the following configuration files:

<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

	<! -- Configure the target object -->
	<bean class="com.sjc.spring.aop.target.UserServiceImpl"></bean>
	<! -- Configure notification classes -->
	<bean id="myAdvice" class="com.sjc.spring.aop.advice.MyAdvice"></bean>
	<! -- AOP configuration -->
	<aop:config>
	
		<! -- <aop:advisor advice-ref="" /> -->
		<aop:pointcut expression="" id="" />
		<aop:aspect ref="myAdvice">
			<aop:before method="before"
				pointcut="execution(* *.. *.*ServiceImpl.*(..) )" />
			<aop:after method="after"
				pointcut="execution(* *.. *.*ServiceImpl.*(..) )" />
		</aop:aspect>
	</aop:config>

</beans>

Copy the code

Entry: DefaultBeanDefinitionDocumentReader# parseBeanDefinitions

We also analyzed this step in the last parsing of SpringIOC source code, so let’s move into

delegate.parseCustomElement(ele);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// Whether the loaded Document object uses Spring's default XML namespace (beans namespace)
		if (delegate.isDefaultNamespace(root)) {
			// Get all child nodes of the root element of the Document object (bean tags, import tags, alias tags, and other custom tags context, AOP, etc.)
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					// The default resolution rules are used for bean tags, import tags, and alias tags
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else { // Elements like context tags, AOP tags, tx tags are resolved using user-defined parsing rulesdelegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code

We entered the: BeanDefinitionParserDelegate# parseCustomElement

This method does two main things:

  • Get the namespace URI (that is, get the value of the XMLNS: AOP or XMLNS: Context property of the beans tag)
  • Get the corresponding handler class according to namespaceUri
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		// Get the namespace URI (that is, get the value of the XMLNS: AOP or XMLNS: Context attribute of the beans tag)
		// http://www.springframework.org/schema/aop
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// Match different NamespaceHandler according to the namespace URI (one namespace corresponds to one NamespaceHandler)
		/ / here will call DefaultNamespaceHandlerResolver resolve method of a class
		// Two steps: Find NamespaceHandler, call NamespaceHandler init (register BeanDefinitionParser for different custom tags)
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

Copy the code

We enter NamespaceHandlerSupport#parse

The main thing we care about with this method is the parser.parse() method, and findParserForElement can be seen as a branch if you’re interested.

public BeanDefinition parse(Element element, ParserContext parserContext) {
		// NamespaceHandler initializes a number of BeanDefinitionParser tags to handle custom tags
		// Match BeanDefinitionParser from the specified NamespaceHandler
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		// Call the parser that specifies the custom tag to complete the specific parsing
		return(parser ! =null ? parser.parse(element, parserContext) : null);
	}
Copy the code

BeanDefinitionParser many implementation class, here we is implemented by ConfigBeanDefinitionParser configuration file.

We entered the ConfigBeanDefinitionParser# parse ();

This method produces three main branches of interest:

  • configureAutoProxyCreator(parserContext, element);

    Is mainly generated AspectJAwareAdvisorAutoProxyCreator class BeanDefinition and registered with the IOC container, this class is used to create the AOP proxy objects

  • parsePointcut(elt, parserContext);

    Produce a AspectJExpressionPointcut BeanDefinition object, and registration. The AspectJExpressionPointcut expression used to resolve our starting point, the reader can look at the beginning we introduce AspectJExpressionPointcut class diagram.

  • parseAspect(elt, parserContext);

    This step mainly wraps the parsed AOP :aspect tag. Let’s focus on this branch.

public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		/ / registered BeanDefinition AspectJAwareAdvisorAutoProxyCreator class: (with the IoC container used to create the AOP proxy objects)
		// BeanPostProcessor can perform some operations on the instantiated bean
		/ / AspectJAwareAdvisorAutoProxyCreator implements the BeanPostProcessor interface, can be instantiated on the target object, create the corresponding proxy objects
		configureAutoProxyCreator(parserContext, element);

		< AOP :aspect>, < AOP: Advisor >, < AOP :pointcut>
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			// Get the node name or element name of the child tag
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				// parse the < AOP :pointcut> tag
				/ / create a AspectJExpressionPointcut BeanDefinition objects, and register
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				// parse the < AOP: Advisor > tag
				/ / create a DefaultBeanFactoryPointcutAdvisor BeanDefinition objects, and register
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				// parse the < AOP :aspect> tag
				// Many BeanDefinition objects are generated
				// AOP: tags such as after correspond to 5 BeanDefinition objects
				// AOP: The method attribute of the after tag corresponds to a BeanDefinition object
				// Final AspectJPointcutAdvisor BeanDefinition class
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}
Copy the code

We entered the ConfigBeanDefinitionParser# parseAspect

This method starts parsing AOP: the aspect tag,

Our primary concern is the parseAdvice method

private void parseAspect(Element aspectElement, ParserContext parserContext) {
		// Get the id attribute value of the 
      
        tag
      
		String aspectId = aspectElement.getAttribute(ID);
		// Get the ref attribute value of the < AOP :aspect> tag, which is the reference name of the enhanced class
		String aspectName = aspectElement.getAttribute(REF);

		try {
			this.parseState.push(new AspectEntry(aspectId, aspectName));
			List<BeanDefinition> beanDefinitions = new ArrayList<>();
			List<BeanReference> beanReferences = new ArrayList<>();
			// Handle the < AOP :declare-parents> child of the < AOP :aspect> tag
			List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
			for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
				Element declareParentsElement = declareParents.get(i);
				beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
			}

			// We have to parse "advice" and all the advice kinds in one loop, to get the
			// ordering semantics right.
			// Get all the children of the < AOP :aspect> tag
			NodeList nodeList = aspectElement.getChildNodes();
			boolean adviceFoundAlready = false;
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				// Check whether < AOP :before>, < AOP :after>, < AOP :after-returning>, < AOP :after-throwing method="">, < AOP :around method=""
				if (isAdviceNode(node, parserContext)) {
					if(! adviceFoundAlready) { adviceFoundAlready =true;
						if(! StringUtils.hasText(aspectName)) { parserContext.getReaderContext().error("<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
									aspectElement, this.parseState.snapshot());
							return;
						}
						beanReferences.add(new RuntimeBeanReference(aspectName));
					}
					
       
      
					// The method does three things:
					// create a RootBeanDefinition based on the weaving methods (before, after, etc.)
					// Write the RootBeanDefinition created in the previous step to a new RootBeanDefinition and construct a new object named advisorDefinition (advisor definition)
					/ / 3, registered to DefaultListableBeanFactory advisorDefinition
					AbstractBeanDefinition advisorDefinition = parseAdvice(
							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
					beanDefinitions.add(advisorDefinition);
				}
			}

			AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
					aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
			parserContext.pushContainingComponent(aspectComponentDefinition);

			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
			for (Element pointcutElement : pointcuts) {
				parsePointcut(pointcutElement, parserContext);
			}

			parserContext.popAndRegisterContainingComponent();
		}
		finally {
			this.parseState.pop(); }}Copy the code

We entered the ConfigBeanDefinitionParser# parseAdvice

This method mainly does is: parsing AOP :before and other five child tags

RootBeanDefinition (adviceDef, adviceDef, adviceDef); RootBeanDefinition (adviceDef, adviceDef, adviceDef); To construct a new object, named advisorDefinition, namely advisor definition 3, registered to DefaultListableBeanFactory advisorDefinition

private AbstractBeanDefinition parseAdvice(
			String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

		try {
			this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

			// create the method factory bean
			
      
        create BeanDefinition object for Method factory Bean: Method object for Advice enhancement class < AOP :brefore Method ="before"
      
			RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
			/ / set MethodLocatingFactoryBean targetBeanName advice for reference name, namely < aop: aspect ref = "myAdvice" > the myAdvice
			methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
			/ / set MethodLocatingFactoryBean methodName to < aop: after > tag method of attribute value (namely method = "before" before, as the advice method name)
			methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
			methodDefinition.setSynthetic(true);

			// create instance factory definition
			BeanDefinition: Used to create instances of the enhanced class, i.e., myAdvice in < AOP :aspect ref="myAdvice">
			RootBeanDefinition aspectFactoryDef =
					new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
			/ / set SimpleBeanFactoryAwareAspectInstanceFactory aspectBeanName to advice the reference name of the class
			aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
			aspectFactoryDef.setSynthetic(true);

			// The above two BeanDefinitions are mainly used to call the Advice object's specified methods through reflection
			// method.invoke(obj,args)

			// register the pointcut
			// The BeanDefinition object (core) of the notification enhancement class, i.e., an < AOP :before>, corresponds to an adviceDef
			AbstractBeanDefinition adviceDef = createAdviceDefinition(
					adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
					beanDefinitions, beanReferences);

			// configure the advisor
			// BeanDefinition object of the notifier class, corresponding to < AOP :aspect>
			RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
			advisorDefinition.setSource(parserContext.extractSource(adviceElement));
			// Set the Advice object property value to the notifier class
			advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
			if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
				advisorDefinition.getPropertyValues().add(
						ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
			}

			// register the final advisor
			// Register advisorDefinition in the IoC container
			parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

			return advisorDefinition;
		}
		finally {
			this.parseState.pop(); }}Copy the code

This is the end of parsing the tag

Proxy object creation analysis

Before we look at AOP proxy object creation, let’s look at the class inheritance structure diagram:

Take a look at the AbstractautoXyCreator class, which contains these methods:

PostProcessBeforeInitialization postProcessAfterInitialization - postProcessBeforeInstantiation AOP functionality entry postProcessAfterInstantiation postProcessPropertyValuesCopy the code

We find analysis entry: AbstractAutoProxyCreator# postProcessAfterInitialization

@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if(bean ! =null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Use dynamic proxy technology to generate proxy objects
				// bean: target object
				// beanName: the name of the target object
				returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
	}
Copy the code

Enter the AbstractAutoProxyCreator# wrapIfNecessary

This approach does three things:

  1. Look for the collection of Advisor objects associated with the proxy class from IOC, because in the previous step we parsed the configuration file and encapsulated the information in several objects and saved it in the IOC container.
  2. Generate proxy objects using JDK dynamic proxies or Cglib dynamic proxies
  3. Put the proxy type into the cache
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		/ / Advice/Pointcut/Advisor/AopInfrastructureBean interface beanClass is not acting as well as within the beanName for aop on aspects of agent
		// See the subclass override shouldSkip() method here
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		// Find the collection of Advisor objects associated with the proxy class, where the point-cut expression is concerned
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// Take the proxy only when the corresponding advisor is not empty
		if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Generate proxy objects using JDK dynamic proxy or cglib dynamic proxy
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// Put the proxy type cache
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
Copy the code

We’re going to go to Abstracta toProxy Create #createProxy

protected Object createProxy(Class
        beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		// Create the proxy factory object
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		// If the CGLib proxy is not used
		if(! proxyFactory.isProxyTargetClass()) {// Whether it is possible to use the CGLib proxy
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				/ / see if beanClass corresponding class contains InitializingBean. The class/DisposableBean class/Aware. The class interface
				// If no, use JDK dynamic proxy. If yes, use CGLib dynamic proxyevaluateProxyInterfaces(beanClass, proxyFactory); }}// Get the set of all associated Advisors (this branch needs to be added)
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		// The targetSource is SingletonTargetSource
		proxyFactory.setTargetSource(targetSource);
		// Empty implementation
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		// Whether to set prefiltering mode. True for this article
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
		// Get objects generated using JDK dynamic proxies or cglib dynamic proxies
		return proxyFactory.getProxy(getProxyClassLoader());
	}
Copy the code

Let’s go to ProxyFactor #getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
		// create a JDK or CGLib AOP proxy
		Call AopProxy to create a Proxy object
		return createAopProxy().getProxy(classLoader);
	}
Copy the code

We chose to create the proxy in the JDK because we implemented the proxy through the interface.

Let’s go to JdkDynamicAopProxy#getProxy

This method does two main things:

  1. Get all proxy interfaces
  2. Invoke the JDK dynamic proxy
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		// Get the full proxy interfaceClass<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// Call the JDK dynamic proxy method
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
Copy the code

At this point, the analysis of the generated proxy class object is completed

Proxy objects perform process analysis

Let’s look at JdkDynamicAopProxy, which implements InvocationHandler, so we’ve already found the entry point for the proxy object to perform the process analysis

Entry: JdkDynamicAopProxy# invoke

This method mainly does the following:

  1. Get all enhancers (Advisors) for the target object

    These advisors are sequential, and they make chain calls in order

  2. If the call chain is empty, the target object’s method is called directly through reflection, that is, without any enhancement to the method

  3. Create instances to call invocation chain ReflectiveMethodInvocation start executing process of AOP interception

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			
      / /... Omit some code

			Object retVal;

			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			// Get the target object
			target = targetSource.getTarget();
			// Get the type of the target objectClass<? > targetClass = (target ! =null ? target.getClass() : null);

			// Get the interception chain for this method.
			// Get all of the enhancers (Advisors) for the target object, which are sequentially called in chain order
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			// Check if we have any notifications. If we don't, we can simply make the reflection invocation to the target class instead of creating the MethodInvocation class

			// Call the method of the target class
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				// Call the target object's method through reflection
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				// We need to create a method call
				// proxy: generates dynamic proxy objects
				// target: target method
				// args: target method parameter
				// targetClass: targetClass object
				// CHAIN: AOP interceptor execution chain, which is a collection of methodInterceptors
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				// Enter the join point through the interceptor chain
				// Start the AOP interception process
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.Class<? > returnType = method.getReturnType();if(retVal ! =null&& retVal == target && returnType ! = Object.class && returnType.isInstance(proxy) && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null&& returnType ! = Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if(target ! =null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.AopContext.setCurrentProxy(oldProxy); }}Copy the code

Let’s look at the process of getting the call chain:

To: AdvisedSupport# getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class
        targetClass) {
		// Create a cache key in unit of method
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		// Get the collection of advisors for the specified method from the cache
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			// Gets the MethodInterceptor collection of the specified methods in the target class, converted from the Advisor
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}
Copy the code

Let’s get the MethodInterceptor collection:

DefaultAdviceChainFactory#getInterceptorsAndDynamicInterceptionAdvice

Here we mainly do:

  1. Create DefaultAdvisorAdapterRegistry instance, and create MethodBeforeAdviceAdapter, AfterReturningAdviceAdapter, ThrowsAdviceAdapter adapter

    Why do you need an adapter here? Let’s look at the following class diagram:

Some Advice is not related to the interceptor MethodInterceptor at all, and you need the adapter to generate the relationship between them. Here is the adapter mode, for example, we bought a lot of models of computers, the use of voltage is not the same, our home voltage is 220v, that how to make our computer can charge on it, nano will need a power adapter. So that makes sense

Matches () with Pointcut ClassFilter().matches() and Pointcut().getmatcher (). If it matches, convert the Advisor to a MethodInterceptor,

3. If you need according to the parameters of the dynamic matching (such as overloading) interceptor in the chain of new InterceptorAndDynamicMethodMatcher

public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class
        targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		// Advice adapter registry
		/ / MethodBeforeAdviceAdapter: Advisor adaptation into MethodBeforeAdvice
		/ / AfterReturningAdviceAdapter: Advisor adaptation into AfterReturningAdvice
		// ThrowsAdviceAdapter: ADAPTS Advisor to ThrowsAdvice
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		// Returns a collection of values that are either Interceptor or its subclass interface MethodInterceptor
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		// Get the target class typeClass<? > actualClass = (targetClass ! =null ? targetClass : method.getDeclaringClass());
		// Whether there is an introduction
		Boolean hasIntroductions = null;

		// The collection of all appropriate advisors obtained for the target method during the generation of the proxy object
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				// If the Advisor can enhance the target class, proceed
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					// Gets a method adapter that matches methods based on the specified pointcut expression
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						// Convert the Advisor to MethodInterceptor
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						The MethodMatcher interface defines two matches() methods via overloading
						Matches () is called static matching. It can be used in most scenarios if the match condition is not too strict
						The matches() method, called static, matches the types of the three arguments dynamically at run time
						// The dividing line between the two methods is the Boolean isRuntime() method
						Matches () matches the Boolean isRuntime() function with a matches() function
						Matches () : matches(); // true; matches();

						// Need to dynamically match parameters (such as overloading)
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(newInterceptorAndDynamicMethodMatcher(interceptor, mm)); }}else{ interceptorList.addAll(Arrays.asList(interceptors)); }}}}else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if(config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); }}else{ Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); }}return interceptorList;
	}
Copy the code

We return to JDkDynamicaOpXy# invoke

Enter the ReflectiveMethodInvocation# proceed (in) :

This is the execution of the call chain

  1. If it goes to the end of the chain, the join point is called directly, that is, the target method is called directly

  2. Will call MethodInterceptor invoke method, in this paper, the configuration file will be called AspectJAfterAdvice, MethodBeforeAdviceInterceptor

    Let’s look at AspectJAfterAdvice: the method call goes like this, and the call chain continues

    public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		finally {
    			invokeAdviceMethod(getJoinPointMatch(), null.null); }}Copy the code

We see: MethodBeforeAdviceInterceptor (pre inform interceptor)

public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}
Copy the code

The target method is executed and then the next call chain is called

This ensures that the order of calls is followed by the pre-notification before the target method and the final notification after the target method executes. It also explains that < AOP :before /> in the configuration file

The order of < AOP :after method=”after”/> does not affect the order of final execution, which is guaranteed by the call chain.

public Object proceed(a) throws Throwable {
		// We start with an index of -1 and increment early.
		// If the execution goes to the end of the chain, the join point method is called directly, i.e. the target method is called directly
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		// Get the MethodInterceptor from the collection
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		/ / if it is InterceptorAndDynamicMethodMatcher type (dynamic)
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<? > targetClass = (this.targetClass ! =null ? this.targetClass : this.method.getDeclaringClass());
			// The target method is matched each time
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				MethodInterceptor's invoke method is called directly if it matches
				/ / note that there is this incoming parameters, we see ReflectiveMethodInvocation types below
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				// If this target method does not apply, proceed to the next chain
				// recursive call
				returnproceed(); }}else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			// The specification applies to the target method and calls the invoke method of the MethodInterceptor directly
			/ / in this namely ReflectiveMethodInvocation instance
			// Pass this in to form a chain of calls
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code

After a long time of writing, finally written, due to the limited level, can not be detailed in all aspects of the reader forgive me, can communicate with me.