Spring 5 Source Deep Parsing - --- Generation of AOP Agent

Keywords: Java JDK Spring Attribute

After obtaining all the enhancements of the corresponding bean s, the agent can be created. Back to the wrapIfNecessary method of AbstractAutoProxyCreator, as follows:

 1 protected static final Object[] DO_NOT_PROXY = null;
 2 
 3 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
 4     if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
 5         return bean;
 6     }
 7     if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
 8         return bean;
 9     }
10     if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
11         this.advisedBeans.put(cacheKey, Boolean.FALSE);
12         return bean;
13     }
14 
15     // Create proxy if we have advice.
16     Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
17     if (specificInterceptors != DO_NOT_PROXY) {
18         this.advisedBeans.put(cacheKey, Boolean.TRUE);
19         Object proxy = createProxy(
20                 bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
21         this.proxyTypes.put(cacheKey, proxy.getClass());
22         return proxy;
23     }
24 
25     this.advisedBeans.put(cacheKey, Boolean.FALSE);
26     return bean;
27 }

In our last article, we analyzed line 16, got all the enhancers for the corresponding beans, and got all the matching Advisor s for the target beans. Next, we will start with line 17. If specific Interceptors are not empty, we will create proxy classes for the current beans. Next, let's look at the method of creating proxy classes.

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);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    // Get the relevant attributes in the current class
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        // Decide for a given bean Should I use it? targetClass Instead of his interface agent,
        // Inspection proxyTargetClass Settings and preserveTargetClass attribute
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // Add Enhancer
    proxyFactory.addAdvisors(advisors);
    // Setting the target class to proxy
    proxyFactory.setTargetSource(targetSource);
    // Custom Agent
    customizeProxyFactory(proxyFactory);
    // Used to control whether modification notifications are allowed after the proxy factory is configured.
    // The default value is false (That is, after the agent is configured, it is not allowed to modify the agent's configuration.). 
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    //The Way to Really Create Agents
    return proxyFactory.getProxy(getProxyClassLoader());
}

@Override
public void setTargetSource(@Nullable TargetSource targetSource) {
    this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
}

public void addAdvisors(Collection<Advisor> advisors) {
    if (isFrozen()) {
        throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
    }
    if (!CollectionUtils.isEmpty(advisors)) {
        for (Advisor advisor : advisors) {
            if (advisor instanceof IntroductionAdvisor) {
                validateIntroductionAdvisor((IntroductionAdvisor) advisor);
            }
            Assert.notNull(advisor, "Advisor must not be null");
            this.advisors.add(advisor);
        }
        updateAdvisorArray();
        adviceChanged();
    }
}

From the above code, we can see that the creation and processing of spring for proxy classes is delegated to ProxyFactory.

Create a proxy

public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

In the above getProxy method, the createAopProxy method is implemented in DefaultAopProxyFactory. The main function of this method is to decide whether to generate Jdk dynamic proxy or Cglib proxy according to optimize, ProxyTargetClass and other parameters. We enter into the method of ____________.

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    // Create a proxy
    return getAopProxyFactory().createAopProxy(this);
}

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.");
        }
        //Manual Settings Creation Cglib After the proxy class, if the target bean Is an interface, but also create jdk proxy class
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        //Establish Cglib agent
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        //Default Creation jdk agent
        return new JdkDynamicAopProxy(config);
    }
}

We know that the agent for Spring is implemented through JDKProxy and CglibProxy. How did Spring choose?

From the judgment conditions of if, we can see that three aspects affect Spring's judgment.

  • optimize: Users are not recommended to use this setting unless they fully understand how AOP agents handle optimization, which is used to control whether agents created through CGLIB use radical optimization strategies. At present, this property is only used for CGLIB proxy and is not valid for JDK dynamic proxy (default proxy).

  • proxyTargetClass: When this property is true, the target class itself is proxied rather than the interface of the target class. If this property value is set to true, the CGLIB agent will be created in the following way: <aop: aspectj-autoproxy-proxy-target-class= "true"/>.

  • HasNoUserSupplied Proxylnterfaces: Is there a proxy interface

The following is a summary of JDK and Cglib modes.

  • If the target object implements the interface, the dynamic proxy of JDK will be used to implement AOP by default.

  • If the target object implements the interface, you can force CGLIB to implement AOP.

  • If the target object does not implement the interface, the CGLIB library must be used, and Spring will automatically convert between JDK dynamic proxy and CGLIB.

How to enforce the use of CGLIB to implement AOP?

(1) Add CGLIB library, Spring_HOME/cglib/*.jar.

(2) Add <aop:aspectj-autoproxy-proxy-target-class="true"/> to the Spring configuration file.

What is the difference between JDK dynamic proxy and CGLIB bytecode generation?

  • JDK dynamic proxy can only generate proxies for classes that implement interfaces, not for classes.
  • CGLIB implements proxy for classes. It mainly generates a subclass for a specified class and overrides the methods in it. Because it is inheritance, it is better not to declare the class or method as final.

Acquisition Agent

This paper mainly introduces the implementation of JDK dynamic proxy class. Before that, it is necessary to familiarize yourself with the example of using JDK proxy. See my previous blog, JDK dynamic proxy source code analysis article.< java Foundation (XVIII) --- Source Code Analysis of java Dynamic Proxy Principle>

Spring's AOP implementation actually uses Proxy and Invocation Handler.

Let's review the way we use JDK proxy again. In the whole process of creating Invocation Handler, the creation of Invocation Handler is the core, and three functions need to be rewritten in the custom Invocation Handler.

  • Constructor that passes in the proxy object.
  • invoke method, which implements all the logic enhanced by AOP.
  • getProxy method, this method is uniform, but essential.

So let's see if Spring's JDK proxy implementation does the same thing. Let's look at the simplified JdkDynamicAopProxy.

  1 final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  2 
  3     private final AdvisedSupport advised;
  4 
  5     public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
  6         Assert.notNull(config, "AdvisedSupport must not be null");
  7         if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
  8             throw new AopConfigException("No advisors and no TargetSource specified");
  9         }
 10         this.advised = config;
 11     }
 12 
 13 
 14     @Override
 15     public Object getProxy() {
 16         return getProxy(ClassUtils.getDefaultClassLoader());
 17     }
 18 
 19     @Override
 20     public Object getProxy(@Nullable ClassLoader classLoader) {
 21         if (logger.isTraceEnabled()) {
 22             logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
 23         }
 24         Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
 25         findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
 26         return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
 27     }
 28 
 29     @Override
 30     @Nullable
 31     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 32         Object oldProxy = null;
 33         boolean setProxyContext = false;
 34 
 35         TargetSource targetSource = this.advised.targetSource;
 36         Object target = null;
 37 
 38         try {
 39             if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
 40                 // The target does not implement the equals(Object) method itself.
 41                 return equals(args[0]);
 42             }
 43             else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
 44                 // The target does not implement the hashCode() method itself.
 45                 return hashCode();
 46             }
 47             else if (method.getDeclaringClass() == DecoratingProxy.class) {
 48                 // There is only getDecoratedClass() declared -> dispatch to proxy config.
 49                 return AopProxyUtils.ultimateTargetClass(this.advised);
 50             }
 51             else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
 52                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
 53                 // Service invocations on ProxyConfig with the proxy config...
 54                 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
 55             }
 56 
 57             Object retVal;
 58 
 59             if (this.advised.exposeProxy) {
 60                 // Make invocation available if necessary.
 61                 oldProxy = AopContext.setCurrentProxy(proxy);
 62                 setProxyContext = true;
 63             }
 64 
 65             // Get as late as possible to minimize the time we "own" the target,
 66             // in case it comes from a pool.
 67             target = targetSource.getTarget();
 68             Class<?> targetClass = (target != null ? target.getClass() : null);
 69 
 70             // Get the interception chain for this method.
 71             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 72 
 73             // Check whether we have any advice. If we don't, we can fallback on direct
 74             // reflective invocation of the target, and avoid creating a MethodInvocation.
 75             if (chain.isEmpty()) {
 76                 // We can skip creating a MethodInvocation: just invoke the target directly
 77                 // Note that the final invoker must be an InvokerInterceptor so we know it does
 78                 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
 79                 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
 80                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
 81             }
 82             else {
 83                 // We need to create a method invocation...
 84                 MethodInvocation invocation =
 85                         new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
 86                 // Proceed to the joinpoint through the interceptor chain.
 87                 retVal = invocation.proceed();
 88             }
 89 
 90             // Massage return value if necessary.
 91             Class<?> returnType = method.getReturnType();
 92             if (retVal != null && retVal == target &&
 93                     returnType != Object.class && returnType.isInstance(proxy) &&
 94                     !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
 95                 // Special case: it returned "this" and the return type of the method
 96                 // is type-compatible. Note that we can't help if the target sets
 97                 // a reference to itself in another returned object.
 98                 retVal = proxy;
 99             }
100             else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
101                 throw new AopInvocationException(
102                         "Null return value from advice does not match primitive return type for: " + method);
103             }
104             return retVal;
105         }
106         finally {
107             if (target != null && !targetSource.isStatic()) {
108                 // Must have come from TargetSource.
109                 targetSource.releaseTarget(target);
110             }
111             if (setProxyContext) {
112                 // Restore old proxy.
113                 AopContext.setCurrentProxy(oldProxy);
114             }
115         }
116     }
117 
118 }

We see that JdkDynamicAopProxy also implements the InvocationHandler interface like our custom InvocationHandler, and provides a getProxy method to create proxy classes and override invoke methods.

Let's focus on the proxy class invocation. If you know about Jdk dynamic proxy, you will know that in order to implement Jdk dynamic proxy function, you need to implement invoke method of InvocationHandler interface (this method is a callback method). When a method in the proxy class is called, it is actually an invoke method called. Let's take a look at the implementation of this method.

 1 @Override
 2 @Nullable
 3 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 4     MethodInvocation invocation;
 5     Object oldProxy = null;
 6     boolean setProxyContext = false;
 7 
 8     TargetSource targetSource = this.advised.targetSource;
 9     Object target = null;
10 
11     try {
12         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
13             return equals(args[0]);
14         }
15         else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
16             return hashCode();
17         }
18         else if (method.getDeclaringClass() == DecoratingProxy.class) {
19             return AopProxyUtils.ultimateTargetClass(this.advised);
20         }
21         else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
22                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
23             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
24         }
25 
26         Object retVal;
27         if (this.advised.exposeProxy) {
28             // Make invocation available if necessary.
29             oldProxy = AopContext.setCurrentProxy(proxy);
30             setProxyContext = true;
31         }
32 
33         target = targetSource.getTarget();
34         Class<?> targetClass = (target != null ? target.getClass() : null);
35 
36         // Get the interceptor chain of the current method
37         List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
38 
39         if (chain.isEmpty()) {
40             // If no interceptor is found, call the pointcut method directly
41             Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
42             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
43         }
44         else {
45             // We need to create a method invocation...
46             // Encapsulate the interceptor in ReflectiveMethodInvocation,
47             // For ease of use proceed Interceptor for Link Table
48             invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
49             // Proceed to the joinpoint through the interceptor chain.
50             // Execution interceptor chain
51             retVal = invocation.proceed();
52         }
53 
54         Class<?> returnType = method.getReturnType();
55         // Return results
56         if (retVal != null && retVal == target &&
57                 returnType != Object.class && returnType.isInstance(proxy) &&
58                 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
59             retVal = proxy;
60         }
61         else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
62             throw new AopInvocationException(
63                     "Null return value from advice does not match primitive return type for: " + method);
64         }
65         return retVal;
66     }
67     finally {
68         if (target != null && !targetSource.isStatic()) {
69             // Must have come from TargetSource.
70             targetSource.releaseTarget(target);
71         }
72         if (setProxyContext) {
73             // Restore old proxy.
74             AopContext.setCurrentProxy(oldProxy);
75         }
76     }
77 }

 

Let's take a look at line 37 to get the enhancer in the target method in the target bean and encapsulate the enhancer into an interceptor chain

 1 @Override
 2 public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
 3         Advised config, Method method, @Nullable Class<?> targetClass) {
 4 
 5     // This is somewhat tricky... We have to process introductions first,
 6     // but we need to preserve order in the ultimate list.
 7     AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
 8     Advisor[] advisors = config.getAdvisors();
 9     List<Object> interceptorList = new ArrayList<>(advisors.length);
10     Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
11     Boolean hasIntroductions = null;
12 
13     //Obtain bean All enhancers in
14     for (Advisor advisor : advisors) {
15         if (advisor instanceof PointcutAdvisor) {
16             // Add it conditionally.
17             PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
18             if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
19                 MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
20                 boolean match;
21                 if (mm instanceof IntroductionAwareMethodMatcher) {
22                     if (hasIntroductions == null) {
23                         hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
24                     }
25                     match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
26                 }
27                 else {
28                     //According to the enhancer's Pointcut Determine whether the enhancer matches the current class method
29                     //We need to know the goal. Bean Not all methods need to be enhanced, but there are some common methods.
30                     match = mm.matches(method, actualClass);
31                 }
32                 if (match) {
33                     //If it matches, the advisor Encapsulated into MethodInterceptor Join in interceptorList in
34                     MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
35                     if (mm.isRuntime()) {
36                         // Creating a new object instance in the getInterceptors() method
37                         // isn't a problem as we normally cache created chains.
38                         for (MethodInterceptor interceptor : interceptors) {
39                             interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
40                         }
41                     }
42                     else {
43                         interceptorList.addAll(Arrays.asList(interceptors));
44                     }
45                 }
46             }
47         }
48         else if (advisor instanceof IntroductionAdvisor) {
49             IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
50             if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
51                 Interceptor[] interceptors = registry.getInterceptors(advisor);
52                 interceptorList.addAll(Arrays.asList(interceptors));
53             }
54         }
55         else {
56             Interceptor[] interceptors = registry.getInterceptors(advisor);
57             interceptorList.addAll(Arrays.asList(interceptors));
58         }
59     }
60 
61     return interceptorList;
62 }

 

We know that not all methods in the target Bean need to be enhanced, so we need to traverse all the Advisor s to determine whether the enhancer matches the method in the current class according to the Pointcut, extract the matching enhancer, encapsulate it as a Method Interceptor, and add it to the interceptor chain. Let's look at line 34.

@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    //Here we traverse three adapters, which will correspond to advisor Conversion to Interceptor
    //The three adapters are MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
}

private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

/**
 * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
 */
public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

@Override
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
    this.adapters.add(adapter);
}

Because Spring involves too many interceptors, enhancers, enhancement methods to enhance logic, we know in the last article that several enhancers created, AspectJAroundAdvice, AspectJAfterAdvice, AspectJAfterThrowing Advice, have implemented MethodInterceptor interfaces, AspectJMethodBeforeAdvice and AJAfterReturnAdvice. The MethodInterceptor interface is not implemented, so AspectJMethodBeforeAdvice and AspectJAfter Returning Advice cannot satisfy the invoke method in the MethodInterceptor interface, so we use the adapter pattern to convert AspectJMethodBeforeAdvice and AspectJAfter Returning Advice into the required MethodInterceptor implementation class.

Traversing through adapters, the corresponding adapter is found through adapter. support advice (advice), and adapter.getInterceptor(advisor) converts the advisor into the corresponding interceptor.

Let's look at these enhancers

AspectJAroundAdvice

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {

    public AspectJAroundAdvice(
            Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJAroundAdviceMethod, pointcut, aif);
    }

    @Override
    public boolean isBeforeAdvice() {
        return false;
    }

    @Override
    public boolean isAfterAdvice() {
        return false;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        if (!(mi instanceof ProxyMethodInvocation)) {
            throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
        }
        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
        ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
        JoinPointMatch jpm = getJoinPointMatch(pmi);
        return invokeAdviceMethod(pjp, jpm, null, null);
    }

}

AspectJMethodBeforeAdvice

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {

    public AspectJMethodBeforeAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }

    @Override
    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }

    @Override
    public boolean isBeforeAdvice() {
        return true;
    }

    @Override
    public boolean isAfterAdvice() {
        return false;
    }

}

AspectJAfterAdvice

public class AspectJAfterAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {

    public AspectJAfterAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }


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

    @Override
    public boolean isBeforeAdvice() {
        return false;
    }

    @Override
    public boolean isAfterAdvice() {
        return true;
    }

}

AspectJAfterReturningAdvice

public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice
        implements AfterReturningAdvice, AfterAdvice, Serializable {

    public AspectJAfterReturningAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }


    @Override
    public boolean isBeforeAdvice() {
        return false;
    }

    @Override
    public boolean isAfterAdvice() {
        return true;
    }

    @Override
    public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
        if (shouldInvokeOnReturnValueOf(method, returnValue)) {
            invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
        }
    }
}

AspectJAfterThrowingAdvice

public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {

    public AspectJAfterThrowingAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }


    @Override
    public boolean isBeforeAdvice() {
        return false;
    }

    @Override
    public boolean isAfterAdvice() {
        return true;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            return mi.proceed();
        }
        catch (Throwable ex) {
            if (shouldInvokeOnThrowing(ex)) {
                invokeAdviceMethod(getJoinPointMatch(), null, ex);
            }
            throw ex;
        }
    }

}

Next, let's look at two adapters, the MethodBeforeAdvice Adapter and the fter Returning Advice Adapter, which adapts the MethodBeforeAdvice and the fter Returning Advice to the corresponding Interceptor.

MethodBeforeAdviceAdapter

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {
        //Judging whether it is MethodBeforeAdvice Type advice
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        //take advice Encapsulated into MethodBeforeAdviceInterceptor
        return new MethodBeforeAdviceInterceptor(advice);
    }
}

//MethodBeforeAdviceInterceptor Realization MethodInterceptor Interface, implementation invoke Method, and advice As an attribute
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

    private final MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }
    
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }

}

AfterReturningAdviceAdapter

class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof AfterReturningAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
        return new AfterReturningAdviceInterceptor(advice);
    }
}

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

    private final AfterReturningAdvice advice;

    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }

}

So far we have obtained a chain of interceptors, including AspectJAroundAdvice, AspectJAfterAdvice, AspectJAfterThrowing Advice, Method BeforeAdvice Interceptor, AfterReturning Advice Interceptor.

Next, the ReflectiveMethodInvocation class encapsulates the chain, and the interceptor is called one by one in the proceed method of the ReflectiveMethodInvocation class. So let's go on to explore how to achieve the pre-enhancement in proceed method, and how to enhance the logic of calling before and after the target method.

Let's first look at the Reflective Method Invocation constructor, which simply assigns attributes, but we need to note that there is a special variable, current Interceptor Index, which represents the subscript for executing Interceptor. Starting with -1, the Interceptor executes one, first +this.current Interceptor Index.

protected ReflectiveMethodInvocation(
        Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
        @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

    this.proxy = proxy;
    this.target = target;
    this.targetClass = targetClass;
    this.method = BridgeMethodResolver.findBridgedMethod(method);
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

private int currentInterceptorIndex = -1;

Following is the ReflectiveMethodInvocation class Process method:

public Object proceed() throws Throwable {
    // First of all, judge whether it's all or not. interceptor(It can also be imagined. advisor)It's all done.
    // The way to judge is to see currentInterceptorIndex The value of this variable is increased to Interceptor There is no total number.
    // If it arrives, the proxy method is executed(invokeJoinpoint());If not, continue to implement Interceptor. 
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // If Interceptor If the execution is not completed, the execution will be taken out. Interceptor,And implement.
    // currentInterceptorIndex Increase first
    Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // If Interceptor yes PointCut type
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        // If the current method meets Interceptor Of PointCut Restrictions on Implementation Interceptor
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
           // Here will be this When variables are passed in, this is a very important point.
            return dm.interceptor.invoke(this);
        }
        // If not, skip the current Interceptor,Execute the next Interceptor
        else {
            return proceed();
        }
    }
    // If Interceptor No PointCut Types are executed directly Interceptor Enhancement inside.
    else {
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

Because of the length process, how the target method and enhancement method are implemented, we will rewrite an article to explain it.

Posted by jonez on Thu, 10 Oct 2019 03:30:58 -0700