preface

The ritual sense of the preface still needs to be there. The main reason for coming back to Spring AOP is that I wrote a conceptual article about AOP before, and then ended the article with a “next source code” analysis. But then he went off to write something else. Er…

In fact, the reason why I am so paranoid about writing Spring AOP is that, after all, it is the focus of the framework, and the abstract code and ideas inside cannot be lost; Besides, we simple programmers, if we can’t actually see the code implementation, how can we stop!

The main purpose of this article is to fully explain how AOP In Spring is implemented. So the order of the whole article is:

  1. The implementation principle of AOP
  2. AOP specific source code analysis
  3. Write a simplified version of AOP to deepen your understanding

Let ‘Go!

Realize the principle of

About the principle, the last article Spring AOP detailed explanation and basic usage ② I have said a little. But for now, I’d like to add.

Spring AOP is implemented in two ways: AspectJ and Spring AOP. The difference is

Spring AOP AspectJ
Only implemented in Java Extended implementation using the Java programming language
No separate compilation process is required Use the AspectJ-specific compiler unless LTW is set
Only run-time weaving is available Runtime weaving is not available. Support compile time, compile time and load time weaving
Method level weaving More powerful – includes fields, methods, constructors, static initializers final classes/methods, and more
Rely on the IOC container to manage the implementation You can apply all objects
Only method execution pointcuts are supported All method cuts
Proxies are created by the target object, and these proxies apply facets Extend at the code level, that is, generate proxy classes before runtime
Much slower than AspectJ Higher performance
Easy to learn and easy to use More complicated

The source code parsing

version

The source code analysis is based on the Spring.5.1.2.release.

parsing

As we all know, the core element of Spring is IOC. So it reserves a lot of extended abstract classes and interfaces. Let’s take a normal Spring flowchart for the bean-managed life cycle:

We can see that by implementing the methods of the abstract class above, we can flexibly configure and change the Bean through the cycle of different phases of the Bean through the methods.

So how does this lifecycle help AOP? Isn’t it reasonable to think that the principle of AOP is dynamic proxying, and that the best time to proxy a Bean is at the appropriate time of its life? Here’s a quick overview of how Spring starts to trigger aOP-related processors:

  1. SpringApplication.run()Start the application.
  2. Methods are called in the run methodrefreshContext()Context preparation and data refreshes
  3. refreshContextThe refresh method that asks up and down the Context is called
  4. refreshThere’s a lot of work in there, but we just need to focusfinishBeanFactoryInitialization()Methods.
  5. finishBeanFactoryInitializationWill be calledpreInstantiateSingletonsmethods
  6. preInstantiateSingletonsMethod will callAbstractBeanFactorygetBeanmethods
  7. getBeanContinue to callAbstractBeanFactorydoGetBeanmethods
  8. doGetbeanWill continue to callAbstractBeanFactorygetSingleton -> getObject -> doGetBean, and finally enter the lambda cycle
  9. The loop is called during the processAbstractAutowireCapableBeanFactory#createBeanMethods.
  10. createBeanThat’s when it gets calledresolveBeforeInstantiationMethods. This method is here and nowGives BeanPostProcessors an opportunity to generate a proxy class instead of the original target class, the exact method that will be called ispostProcessBeforeInstantiationpostProcessAfterInitialization.
  11. applyBeanPostProcessorsBeforeInstantiationMethod by getting all of the current contextBeanPostProcessorsLoop through, and then call itpostProcessBeforeInstantiationpostProcessAfterInitialization.

Ok! Although we omit the process, we have now come to an important step. At this point, it’s time to actually scan the generated beans. There is a call in the BeanPostProcessors AnnotationAwareAspectJAutoProxyCreator classes. It implements the InstantiationAwareBeanPostProcessor (belong to BeanPostProcessor subclass, the main function is before instantiated attribute is set to automatic assembly). AnnotationAwareAspectJAutoProxyCreator also instantiate AbstractAutoProxyCreator (belong to BeanPostProcessor can expand the AOP proxy abstract class). Let’s take a look at its postProcessBeforeInstantiation method

It is important to note that because of the Spring Boot loading context, the loaded class does not match the source code parsing, so it may be in a different context

public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) {
    // Get the bean's already cached name
    Object cacheKey = getCacheKey(beanClass, beanName);
    // Check whether the load has been loaded according to the name
    if(! StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        / / check whether about AOP related classes | | determine whether need to skip loading
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null; }}/ / get TargetSource
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if(targetSource ! =null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}
Copy the code

The steps of the code above are

  1. throughisInfrastructureClassJudge whether or notAOPRelated to the class
  2. throughshouldSkipTo determine whether to skip thisBeanThe agent of
  3. If all the above pass, then passgetAdvicesAndAdvisorsForBeanMethods To obtain the corresponding section information
  4. The proxy class is generated from the aspect information and then returned to facilitate the replacement of the original object class

As a sidebar, the TargetSource in the above code is actually a Holder that holds the object. The advantage of this is that the real proxy is not the actual object but the TargetSource, so we can replace the object held by the TargetSource without having to regenerate the proxy.

So let’s first look at how the shouldSkip method determines if we need to generate a proxy class; And then see how getAdvicesAndAdvisorsForBean resolution to find the corresponding information of section; Finally, see how createProxy generates the proxy class in Spring.

shouldSkip

protected boolean shouldSkip(Class
        beanClass, String beanName) {
    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

The above code is mainly through the findCandidateAdvisors method to obtain the names of all aspects, and the loop comparison is whether the same; Return true if the same. And is the method of adjustable BeanFactoryAdvisorRetrievalHelper main findCandidateAdvisors method. Note that the current class is a global singleton. Its purpose is to find all appropriate Advisors in the current beanFactory, but not FactoryBeans.

public List<Advisor> findAdvisorBeans(a) {
    // Store the name of the advisors
    String[] advisorNames = null;
    synchronized (this) {
        advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // No FactoryBeans are initialized here, just normal, unloaded classes
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true.false);
            this.cachedAdvisorBeanNames = advisorNames; }}if (advisorNames.length == 0) {
        return new LinkedList<Advisor>();
    }

    List<Advisor> advisors = new LinkedList<Advisor>();
    // Loop the name of adivors
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // If you want to check whether the creation is excluded or is being created
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                // log info
            }
            else {
                / / add
                try {
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    // throw Exception}}}}return advisors;
}
Copy the code

The above is mainly to find the Advisor class from the BeanFactory and make a judgment.

The beans returned by the findCandidateAdvisors() method can be cached, which helps speed up retrieval

getAdvicesAndAdvisorsForBean

In AbstractAutoProxyCreator getAdvicesAndAdvisorsForBean method is a template method, mainly is reserved for the subclass to be realized. Let’s take a look at its subclasses AbstractAdvisorAutoProxyCreator implementation.

	protectedObject[] getAdvicesAndAdvisorsForBean(Class<? > beanClass, String beanName, TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}
Copy the code

Is to call the AbstractAdvisorAutoProxyCreator findEligibleAdvisors method.

	protected List<Advisor> findEligibleAdvisors(Class
        beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if(! eligibleAdvisors.isEmpty()) {// Sort the cuts
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
    
	protected void extendAdvisors(List<Advisor> candidateAdvisors) {
		AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
	}
Copy the code

The above code is used to find the Advisor for the appropriate Bean. The main steps are:

  1. Get all ofAdvisors
  2. throughfindAdvisorsThatCanApplyMethod will beAdvisorsbeanNameBegin to match
  3. throughextendAdvisorsTo matchAdvisorMake a small extension (that is, add a header Advisor)
  4. Sort last (for sequential execution)

Below I will divide the title to carry on the explanation!

Get all Advisors

Let’s first look at how to get all the Advisors. This method is invoked the AnnotationAwareAspectAutoProxyCreator findCandidateAdvisors method.

protected List<Advisor> findCandidateAdvisors(a) {
    // Get it according to the parent rules
    / / in fact, that is, the above advisorRetrievalHelper. FindAdvisorBeans () call
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Add all section classes annotated with @aspect
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    return advisors;
}
Copy the code

Match with findAdvisorsThatCanApply

And then let’s see how they match.

protected List<Advisor> findAdvisorsThatCanApply( List
       
         candidateAdvisors, Class
         beanClass, String beanName)
        {
    // Set beanName with ThreadLocal
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null); }}Copy the code

AopUtils. FindAdvisorsThatCanApply match

public static List<Advisor> findAdvisorsThatCanApply(List
       
         candidateAdvisors, Class
         clazz)
        {
    // Omit some judgment code
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceofIntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); }}booleanhasIntroductions = ! eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // Skip it, the above code is already loaded
            continue;
        }
        if(canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); }}return eligibleAdvisors;
}
Copy the code

The core code above is the canApply method, which is primarily used to filter inappropriate advisors. Source code analysis is as follows:

public static boolean canApply(Advisor advisor, Class<? > targetClass,boolean hasIntroductions) {
    // In case of IntroductionAdvisor, filter with PointCut
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // Returns true by default if there are no matches
        return true; }}Copy the code

Extend with extendAdvisors

Let’s see what extendAdvisors actually do. Mainly is to call the AspectJProxyUtils makeAdvisorChainAspectJCapableIfNecessary extension code. The purpose of this method is to add ExposeBeanNameAdvisors, a special Adviors, to the chain of Advisors just filtered. The role of the exposebeanna ameadvisors is to pass the MethodInvocation when that step is called. Let’s briefly look at the code:

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // If it is empty, no execution is required
    if(! advisors.isEmpty()) {boolean foundAspectJAdvice = false;
        for (Advisor advisor : advisors) {
            // Be careful not to get the Advice without a guard, as
            // this might eagerly instantiate a non-singleton AspectJ aspect
            // Check whether it is cut
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true; }}/ / if there is no ExposeInvocationInterceptor is added
        if(foundAspectJAdvice && ! advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true; }}return false;
}

private static boolean isAspectJAdvice(Advisor advisor) {
    return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
            advisor.getAdvice() instanceof AbstractAspectJAdvice ||
            (advisor instanceof PointcutAdvisor &&
                     ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}
Copy the code

Sort advisors

And finally, let’s see what sort is mostly done by elements. In Spring Boot, the main sorting is done with the @Order annotation. Through the comparator AnnotationAwareOrderComparator implementation. Take a look at its findOrder method.

createProxy

The createProxy method is implemented by AbstractAutoProxyCreator.

If you need another extension to AOP, you can further enhance it by inheriting BeanNameAutoProxyCreator or AbstractAutoProxyCreator

I want to talk a little bit about what createProxy basically does. Step is

  1. Create ProxyFactory
  2. Judgment determines whether targetClass should be used instead of its interface proxy for a given bean
  3. Create the Advisor using the buildAdvisors method
  4. The information for the proxyFactory setting includes advisors, target objects, whether subclasses are allowed to pre-filter the Advisor that needs to be executed, and configuring the proxyFactory by calling the template method customizeProxyFactory
  5. Finally, call ProxyFactory.getProxy for dynamic proxy
protected Object createProxy( Class
        beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
	/ / if it is ConfigurableListableBeanFactory TargetSource can be exposed
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
    // Create a ProxyFactory to copy the configuration information of the slice
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if(! proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else{ evaluateProxyInterfaces(beanClass, proxyFactory); }}// Encapsulate the enhancer
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // Set the class to proxy
    proxyFactory.addAdvisors(advisors);
    // Set the class to proxy
    proxyFactory.setTargetSource(targetSource);
    // Provides a custom function customizeProxyFactory for subclasses
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code

ok! The above code Outlines the loading process. You said, “What? Isn’t Spring AOP based on JDK Dynamic Proxy and Cglib? Why don’t I see any code? I had no problem memorizing in the interview.” Now don’t panic, I’m going to talk about this in isolation, because it’s important and it’s long.

ProxyFactory.getProxy()

ProxyFactory/ProxyCreatorSupport/AdvisedSupport ProxyConfig/Advised

class instructions
ProxyFactory Decorates utility classes and provides apis externally
ProxyCreatorSupport Proxy base factory, which provides configurable AopProxyFactory and proxy listeners
AdvisedSupport Base class for the AOP proxy configuration manager, providing interceptor chains, interface caching, whether to filter parameters ahead of time, and so on
ProxyConfig Highest parent class to ensure global parameter consistency

Now that we’ve set the properties of ProxyFactory, let’s call getProxy to get the proxy class.

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}
Copy the code

CreateAopProxy is the aopProxyFactory set in ProxyCreatorSupport. If you look at createAopProxy you’ll see that it passes itself in as a parameter, and that’s what AdvisedSupport does.

Moving forward, we call the getProxy() method.

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<? > targetClass = config.getTargetClass();if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return newJdkDynamicAopProxy(config); }}Copy the code

What is actually returned is AopProxy. AopProxy has two subclasses: CglibAopProxy and JdkDynamicAopProxy. This is the JDK proxy and the Cglib proxy.

Let’s see what JdkDynamicAopProxy looks like. In the source code, JdkDynamicAopProxy implements the InvocationHandler interface, which also meets the Java native dynamic proxy implementation standards. Since InvocationHandler is implemented, its invoke method must also be invoked when its target method is called. Let’s take a look

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

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

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // Call equals
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // If the method called is hashCode
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        // Both the DecoratingProxy method and the Advised interface method end up calling config
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // call by reflection
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
		// Check whether "expose proxy" is set to true
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        Try to delay fetching the object as long as possible, since the target object may come from the object pool or elsewheretarget = targetSource.getTarget(); Class<? > targetClass = (target ! =null ? target.getClass() : null);

        // Get the call chain for the current method
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // If the chain is empty, call directly
        if (chain.isEmpty()) {
            // reduce the chance to create MethodInvocation, just call the method directly
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            / / create ReflectiveMethodInvocation, this is for the sake of combining invocation chain as well as the method to carry on the prototype chain calls
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Handle JoinPoint through the method's call chain
            retVal = invocation.proceed();
        }

        // Get the type of the value returned by the method, and process the returned informationClass<? > returnType = method.getReturnType();if(retVal ! =null&& retVal == target && returnType ! = Object.class && returnType.isInstance(proxy) && ! RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// If the return value is equal to the target object, only the Proxy object can be returned directly
            retVal = proxy;
        }
        else if (retVal == null&& returnType ! = Void.TYPE && returnType.isPrimitive()) {//throw Exception
        }
        return retVal;
    }
    finally {
        if(target ! =null && !targetSource.isStatic()) {
            // Set aside template methods to free objects (usually those with object pools need to implement this method)
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Reset to the old proxy objectAopContext.setCurrentProxy(oldProxy); }}}Copy the code

ExposeProxy mainly solves the problem of nested method proxy. For example

class A {
	@Transactional(propagation = Propagation.REQUIRED)
    public void a(a) {
    	this.b();
    }
    
    @Transactional(propagation = Propagation.REQUIRED_NEW)
    public void b(a) {
    	System.out.println("method b"); }}Copy the code

In the above example, method B is not intercepted by the section. Method B is an internal call to method A. Then if method B also implements AOP then only through the expose-proxy attribute.

This is the JdkDynamicAopProxy proxy. Let’s take a look at the ObjenesisCglibAopProxy that belongs to Cglib. The Cglib approach is created with intercepting methods ready to generate proxy classes.

public Object getProxy(ClassLoader classLoader) {
     //throw code

    try{ Class<? > rootClass =this.advised.getTargetClass(); Class<? > proxySuperClass = rootClass;if (ClassUtils.isCglibProxyClass(rootClass)) {
            proxySuperClass = rootClass.getSuperclass();
            // Loop the parent interface, saveClass<? >[] additionalInterfaces = rootClass.getInterfaces();for(Class<? > additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface); }}/ / check
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB enhancement
        Enhancer enhancer = createEnhancer();
        if(classLoader ! =null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
		// Get the interception method according to ClassCallback[] callbacks = getCallbacks(rootClass); Class<? >[] types =newClass<? >[callbacks.length];for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap is called after getCallbacks
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy and instantiate it
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    
    //throw Exception
}
Copy the code

In the process of acquiring the Proxy, we will get the corresponding interception method according to TargetSource. The method to get this is getCallbacks.

privateCallback[] getCallbacks(Class<? > rootClass)throws Exception {
    // Optimized parameters
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isFrozen = this.advised.isFrozen();
    boolean isStatic = this.advised.getTargetSource().isStatic();

    // Select the AOP interceptor
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

    // Whether to expose the object
    Callback targetInterceptor;
    if (exposeProxy) {
        targetInterceptor = isStatic ?
                new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
    }
    else {
        targetInterceptor = isStatic ?
                new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
    }

    Callback targetDispatcher = isStatic ?
            new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();

    Callback[] mainCallbacks = new Callback[] {
            aopInterceptor,  // for normal advice
            targetInterceptor,  // invoke target without considering advice, if optimized
            new SerializableNoOp(),  // no override for methods mapped to this
            targetDispatcher, this.advisedDispatcher,
            new EqualsInterceptor(this.advised),
            new HashCodeInterceptor(this.advised)
    };

    Callback[] callbacks;

    // If the target is static and the notification chain is frozen, then we can do some optimization by sending an AOP call, using a fixed chain to point the method directly to the target.
    if (isStatic && isFrozen) {
        Method[] methods = rootClass.getMethods();
        Callback[] fixedCallbacks = new Callback[methods.length];
        this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);

        // loop method
        for (int x = 0; x < methods.length; x++) {
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
            fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                    chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
            this.fixedInterceptorMap.put(methods[x].toString(), x);
        }

        / / copy callbacks
        callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
        System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
        System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
        this.fixedInterceptorOffset = mainCallbacks.length;
    }
    else {
        callbacks = mainCallbacks;
    }
    return callbacks;
}
Copy the code

After a series of configurations, Spring generates and instantiates the proxy.

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { Class<? > proxyClass = enhancer.createClass(); Object proxyInstance =null;

    if (objenesis.isWorthTrying()) {
        try {
            proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
        }
        catch (Throwable ex) {
            //throw Exception}}if (proxyInstance == null) {
        // If proxyInstance is null, use the normal constructor to initialize it
        try {
            proxyInstance = (this.constructorArgs ! =null ?
                    proxyClass.getConstructor(this.constructorArgTypes).newInstance(this.constructorArgs) :
                    proxyClass.newInstance());
        }
        catch (Throwable ex) {
            //throw Exception
        }
    }

    ((Factory) proxyInstance).setCallbacks(callbacks);
    return proxyInstance;
}
Copy the code

Finally, Cglib, like JDK Proxy, calls an invoke-like method. There are several main implementations in Cglib:

The name of the class instructions
StaticUnadvisedInterceptor A static method interceptor invocation without advise
StaticUnadvisedExposedInterceptor There is no static method interceptor for advise, but one that exposes proxy
DynamicUnadvisedInterceptor Dynamic method interceptor calls without advise
DynamicUnadvisedExposedInterceptor There is no dynamic method interceptor for Advise, but one that exposes proxy
StaticDispatcher Make sure the return value is not this
EqualsInterceptor Equals method interceptor
HashCodeInterceptor HashCode method interceptor
FixedChainStaticTargetInterceptor The interceptor is dedicated to advise on frozen static agents
DynamicAdvisedInterceptor There is a dynamic method interceptor for advise

Because we have most commonly is advise dynamic methods interceptors, so see DynamicAdvisedInterceptor# intercept method.

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false; Class<? > targetClass =null;
    Object target = null;
    try {
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // Delay fetching the object, since the target object may come from the object pool
        target = getTarget();
        if(target ! =null) {
            targetClass = target.getClass();
        }
        // Get the call chain
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        Call the method directly if there is no advice
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
            // create the CglibMethodInvocation and invoke it
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        / / release
        if(target ! =null) {
            releaseTarget(target);
        }
        // Reset the old Proxy
        if(setProxyContext) { AopContext.setCurrentProxy(oldProxy); }}}Copy the code

A simple AOP example

From the above source code parsing, we know that Spring AOP is really just four steps:

  1. When the application starts, yeahAOPThe relevant configuration classes are read
  2. Then throughSpringExtension function that handles the target object that needs to be proxied
  3. The interceptor chain is obtained based on the target object, and then the parameter is assigned
  4. Finally, the proxy instantiation returns to replace the original target class

We can also implement an AOP example that mimics Spring.

  1. First we create a section classAspects, which contains all kinds of notifications (pre, post, etc., benchmarkingSpringMethodBeforeAdvice)
  2. Then we wrote the implementationAspectsSimpleAspectThe default interception passes.
  3. And then we write oneLogAspectinheritanceSimpleAspecttheLogAspectYou can override methods of interest to the parent class itself
  4. Then we write one to useJDK Dynamic ProxyThe proxy generates classes forJdkDynamicAopProxy(For SpringJdkDynamicAopProxy)
  5. And then we write oneTargetSourceActing as a proxy data source
  6. The foundation aboveAOPWith the code finished, we started to write a business interface and implementation classUserServiceUserServceImpl(Remember becauseJDK Dynamic ProxyInterface required)
  7. Finally, we write test classes to test

The first step

public interface Aspects {

    boolean before(Object target, Method method, Object[] args);

    boolean after(Object target, Method method, Object[] args);

    boolean afterException(Object target, Method method, Object[] args, Throwable e);

}
Copy the code

‘the second step

public class SimpleAspect implements Aspects{
    @Override
    public boolean before(Object target, Method method, Object[] args) {
        return true;
    }

    @Override
    public boolean after(Object target, Method method, Object[] args) {
        return true;
    }

    @Override
    public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
        return true; }}Copy the code

The third step

public class LogAspect extends SimpleAspect{
    @Override
    public boolean before(Object target, Method method, Object[] args) {
        System.out.println("log in the time " + System.currentTimeMillis());
        return true; }}Copy the code

The fourth step

public class JdkDynamicAopProxy implements InvocationHandler {

    private TargetSource target;
    private Aspects aspect;

    / / the constructor
    public JdkDynamicAopProxy(TargetSource target, Aspects aspect) {
        this.target = target;
        this.aspect = aspect;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        final Object target = this.target.getTarget();
        final Aspects aspects = this.aspect;

        Object result = null;
        if (aspects.before(target, method, args)) {     // call the prefix
            result = method.invoke(target, args==null ? null : args);
        }

        if (aspects.after(target, method, args)) {  / / rear
            return result;
        }
        return null; }}Copy the code

Step 5

public class TargetSource {
    private Object target;
    public Object getTarget(a) {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target; }}Copy the code

Step 6: UserService interface

public interface UserService {
    public void show(a);
}
Copy the code

The implementation class

public class UserServiceImpl implements UserService{
    public void show(a){
        System.out.println("User create"); }}Copy the code

The last step

public class AopTest {
    public static void main(String[] args) {
        // Create a slice
        Aspects aspects = new LogAspect();
        TargetSource targetSource = new TargetSource();
        UserServiceImpl userService = new UserServiceImpl();
        targetSource.setTarget(userService);
        // Create the proxy class and call the method
        JdkDynamicAopProxy jdkProxyIntercept = new JdkDynamicAopProxy(targetSource, aspects);
        UserService userService1 = (UserService)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{UserService.class}, jdkProxyIntercept);
        userService1.show();
    }
}
Copy the code

The output is

log in the time 1605582637979
User create
Copy the code

The example above is a very simple complete example, so it doesn’t consider very much, for example

  1. Resolve nested notifications (via chain of responsibility invocation)
  2. Consider the interception method if yesequals,hashCode, static methods, etc
  3. Right now it’s hard coded,AOPHow about adding interceptors anytime and anywhere?
  4. And more…

It is recommended that those who want to dig deeper take a look at every detail of the Spring AOP implementation.

conclusion

At this point, the source code for Spring AOP is complete. To be honest, there are concepts or problems that can only be discovered through the source code. And in the process of looking at the source code, generally speaking, not only need to grasp the general process, but also have to ask for certain details, in order to more profound grasp.