spring source series 8: creation of AOP source resolution agent

review

First review:

JDK dynamic agent and CGLIB dynamic agent

The difference between the instantiation awarebeanpostprocessor and bean postprocessor in Spring

We learned that
Two elements of JDK dynamic proxy: Proxy+InvocationHandler
Two elements of CGLB dynamic agent: Enhancer + MethodInterceptor(Callback)

Spring AOP is implemented by dynamic proxy and CGLB proxy. That is to say, the final goal of spring should also be on the Proxy+InvocationHandler or Enhancer + MethodInterceptor.

With this expectation, let's see how spring organizes AOP and builds an AOP system on top of dynamic agents.

Concept understanding:

  • Notice: notice can understand the enhancer, which is the extra work.
  • Join point: a point that allows tangent insertion. This point can be a method call, exception thrown, etc.
  • Tangent point: not all connection points need to be inserted by tangent, tangent point is to define which connection points can be inserted into tangent.

The conclusion is: select some connection points from all connection points, intercept them and perform additional operations.

Understanding of components:

AOP was first proposed by the AOP alliance organization, and a set of specifications was formulated. Spring introduced the AOP idea into the framework, and must abide by the AOP alliance specifications.

Note: there are so many components. I only list the components that I think involve the understanding of the main line.

AOP Federation specification definition component

Notice (enhancer): don't get tangled up in too much. It is uniformly understood as interceptor or enhancer.

Note: the MethodInterceptor here is different from the one mentioned above
 org.springframework.cglib.proxy.MethodInterceptor don't mix.

Connection point (intercepted): spring mostly intercepts method calls, which can be understood as method calls.

spring definition component

1.Pointcut: defines which connection points can be inserted into the tangent. Two matchers, ClassFilter and MethodMatcher, are provided to match connection points.

2.Advisor: package Advice and Pointcut. Semantics: where to intercept, what kind of enhancer to insert (what to do) Advisor=Advice+Pointcut
Divided into

  • Introduction Advisor: introduction enhancement, which enables a class to have the function of an interface without modifying it.
  • Pointcut Advisor: advisor related to pointcut

3.TargetSource: encapsulation of the target object.

4.Advised interface: package Advisor and TargetSource.
Implementation class AdvisedSupport: from its two properties, we can see that it encapsulates Advisor and TargetSource.

  • Targetsource Targetsource = empty? Target? Source; indicates the target object
  • private List advisors = new ArrayList(); store Advisor

5.AopProxy: AOP proxy object

  • Sub JDK proxy (JdkDynamicAopProxy)
  • Cglib proxy (CglibAopProxy)

Both JdkDynamicAopProxy and CglibAopProxy implement the getProxy() method of AopProxy to return the real proxy object.

6.AopProxyFactory: AOP proxy policy factory class. Depending on the condition, choose whether to use JDK or Cglib to create the proxy object.

7.ProxyCreatorSupport: it is semantically understood to support the creation of proxy objects. Let's look at how he supports his inheritance.

  • ProxyConfig: provides aop configuration properties.
  • Advise dsupport: inherits ProxyConfig and implements the Advised interface. Encapsulate advice and TargetSource and provide actions for them
  • ProxyCreatorSupport: including AopProxyFactory, which provides the creation of JdkDynamicAopProxy and CglibAopProxy.

8.AbstractAutoProxyCreator: inherit relationship from it. Let's analyze this component.

  • First of all, he is a (Bean processor) instantiaionawarebeanpostprocessor, so he will be executed during the Bean production line createBean() of spring. Intercept during Bean definition creation and create proxy objects as required.
  • Aware: you can get the BeanFactory warehouse and the BeanClassLoader loader.
  • ProxyCreatorSupport, which inherits the function of creating agent.

About the understanding of BeanPostProcessor and instantiaionawarebeanpostprocessor, or deep understanding The difference between the instantiation awarebeanpostprocessor and bean postprocessor in Spring.

Summary:

If you summarize the relationships between the following components:

Developers define facet information -- "spring resolves Advice, pointcut and other configuration information --" AopProxyFactory creates proxy object according to configuration -- "proxy method executes.

This is also the main work of spring AOP.

In general:

  • Analysis of the definition of "1":
  • Creation of agent 2:
  • Execution of "3" agent:

Let's focus on definition resolution and proxy creation.

Generation process of agent:

Execution process of agent:
Let's talk about the AOP process in the spring boot environment.

1. Configure the extender of AOP to intercept the process of Bean creation by beandefinition: AnnotationAwareAspectJAutoProxyCreator:

Configure the AOP environment first

@The @ import (aspectjautoproxyregister. Class) on the EnableAspectJAutoProxy annotation introduces the aspectjautoproxyregister class. Aspectjautoproxyregister implements the importbeandefinitionregister interface. So its registerBeanDefinitions() will have some operations to inject beandefinition. The registerBeanDefinitions() method will inject an annotationawareaspectjautoproxyc into the warehouse Reator.

@Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

//Inject AnnotationAwareAspectJAutoProxyCreator extender
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }

AnnotationAwareAspectJAutoProxyCreator indirectly inherits the AOP extension tool AbstractAutoProxyCreator, which is a BeanPostProcessor.

AbstractAutoProxyCreator:

  • It is a Bean processor, which can intercept the Bean creation process.
  • Inherited ProxyCreatorSupport's ability to create proxy classes.

This way: AOP environment.

2. Intercept production process and create AOP object:

In the process of creating a Bean, the createBean() production line will execute all beanpostprocessors. Annotationawareaspectjautoproxycreator (abstractautoproxycreator) as a BeanPostProcessor.

stay The difference between the instantiation awarebeanpostprocessor and bean postprocessor in Spring As I've said, there are five methods of BeanPostProcessor that implement the interface of instantiaawarebaanpostprocessor, which are followed by the Bean creation process and execution order.
postProcessBeforeInstantiation() -->postProcessAfterInstantiation-->postProcessPropertyValues-->postProcessBeforeInitialization()-->postProcessAfterInitialization()

Process:

(2.1) the first step is to execute the postprocessbeforeinstance instantiation preprocessing method.

@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(beanClass, beanName);
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
        if (beanName != null) {
            TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }
        return null;
    }

It is mainly divided into two parts:
(2.1.1) first determine whether the agent should be

  • Is infrastructure class (bean class) if it is an AOP infrastructure class, it is not a proxy. It means that AOP related definition classes such as Advice, Pointcut and Advisor should not create agents.
  • shouldSkip() should skip the class, not proxy. Judgment basis: it is not skipped by default. Subclass may override its judgment basis. AnnotationAwareAspectJAutoProxyCreator, the parent class of AnnotationAwareAspectJAutoProxyCreator, overrides the shouldSkip method (1) with the resolution of the section definition.

Focus on shouldSkip()

/*
1.First, find the Advisor suitable for the current Bean. Find it through findCandidateAdvisors().
        findCandidateAdvisors()Advisors will be obtained through two channels.
            > BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans():It is to find the Advisor that implements the Advisor class from the warehouse
            > BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()The Aspect class of @ Aspect annotation will be taken from the warehouse and the Advisor will be parsed.
 
     Let's focus on building aspectjadvisers
            >Traverse the BeanDefinition in the BeanFactory warehouse, and judge whether it is isAspect() according to its type: judge whether the class is annotated by @ Aspect and has not been compiled by aspectj
            >If so, create an (aspectjadviserfactory) beanfactoryaspectinstancefactory factory object based on the current BeanDefinition.
            >Create a list using the aspectjadviserfactory factory < advisor >
            >As mentioned above, advisor = advice + pointcut. Advice is the encapsulation of notifications (@ before...), and pointcut is the encapsulation of pointcuts.
         
    Summary: this time I know what it looks like to define a tangent class and be parsed.

2.Judge whether there is current BeanDefinition in the candidate advisors found. If so, skip.


*/
@Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // TODO: Consider optimization by caching the list of the aspect names
        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);
    }

(2.1.2) determine whether there is a custom TargetSource.
If there is a custom TargetSource, put the current beanName into the targetSourcedBeans cache, and directly go to the branch of creating agent, instead of going to create Bean with createBean, this is an opportunity. We will not talk about the branch of custom TargetSource for now.

if (beanName != null) {
            TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }

Summarize the work content of postprocessbeforeinstance:

  • Definition of analytic section
  • Provide an opportunity to return the proxy object directly without going through the createBean() pipeline. Custom TargetSource

(2.2)postProcessAfterInstantiation

  • return true ;

(2.3)postProcessBeforeInitialization

  • Returned Bean is not processed

(2.4)postProcessAfterInitialization() (click on the blackboard, key points)

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

The key is the wrap if Necessary method: wrap if Necessary
This method can be divided into two parts:

  1. Conditions met:
  2. Packing:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        //1. Custom TargetSource. Proxy subclass generation has been done. Return Bean instance directly without wrapping
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        //2. If it has been determined that no agency is needed, no agency
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        //3. Isinstructionclass (bean. Getclass()) is the non proxy of infrastructure class
        //4.shouldSkip(bean.getClass(), beanName) should be skipped without proxy
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        //5. No agent without specific interceptor
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

Let's analyze these two parts one by one.

(2.4.1) meet the conditions

Non agency:

  • Custom TargetSource, proxy subclass generation has been done. Return the Bean instance directly without wrapping. ()
  • If it has been determined that no agent is needed, no agent is required. Return Bean instance directly
  • Isinstructionclass (bean. Getclass()) is the non proxy of infrastructure class. (mentioned above)
  • shouldSkip(bean.getClass(), beanName) should skip the non proxy (mentioned above)
  • No agent without specific interceptor

Agent's situation:

  • Agent with specific Advice: the return of getAdvicesAndAdvisorsForBean() is not empty. (Create proxy if we have advice.)

Advisor search: that's the point.

That is, getAdvicesAndAdvisorsForBean method. Here, we go to the container to find the Advisor suitable for the current bean. The final call is

AbstractAdvisorAutoProxyCreator.findEligibleAdvisors:

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }
  • Findcandidate advisors: get all the advisors, as mentioned earlier. Because the cache is made, it is taken directly from the cache.

  • Findadvisors that can apply: look at the parameters it has passed in, candidate advisors. That is to say, this method must be to find all the candidate advisors that are suitable for the current Bean.
  • Extend advisor s: allow subclasses to add Advisors

Applicability judgment
findAdvisorsThatCanApply finally calls aoutilities.findAdvisorsThatCanApply:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
        //First pass
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
        //First pass
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            }
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

This method has two for loops.
The first for loop looks for an advisor of type introduction advisor and calls aoutilities.canapply
The second pass of the for loop is to find an ordinary advisor and call AopUtils.canApply.

AopUtils.canApply makes different judgments for two types of Advisor:

  • For the advisor of introduction advisor type, you only need to verify whether the ClassFilter of advisor matches the current class.
  • For ordinary advisor s: (1) first check whether the specified class is within the matching range of Pointcut; (2) if yes, check whether any method of this kind can be matched, and if yes, return true; (3) if no method can be matched, all methods of targetclass (detected class) will be obtained by reflection method and handed to MethodMatcher.matches method of Pointcut for detection one by one.
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }
        //Whether to configure any method
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) {
            // No need to iterate the methods if we're matching any method anyway...
            return true;
        }

        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }
        //One by one investigation
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class<?> clazz : classes) {
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                if ((introductionAwareMethodMatcher != null &&
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }

        return false;
    }

Here, AopUtils.canApply returns true. findAdvisorsThatCanApply() is to find the Advisors that can be applied to the current class. Then extend the Advisors. After sorting the Advisors that can be applied to the current class, getAdvicesAndAdvisorsForBean completes the work.

getAdvicesAndAdvisorsForBean() returns if it is not Null. Then there's the packaging.

(2.4.2) packaging
After getAdvicesAndAdvisorsForBean returns that the advisors are not null, you can create a proxy.

protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);

        if (!proxyFactory.isProxyTargetClass()) {
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

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

        return proxyFactory.getProxy(getProxyClassLoader());
    }
  • First, create the ProxyFactory factory factory and copy the agent properties from AbstractAutoProxyCreator. Because AbstractAutoProxyCreator inherits ProxyConfig, it is also a agent configuration class.
  • Determine whether it is based on class proxy or interface proxy. If based on the interface proxy, get the interface and set it to ProxyFactory.
  • Buildaadvisors() method, which integrates the advisors and AbstractAutoProxyCreator. interceptorNames attributes found by getAdvicesAndAdvisorsForBean, and the enhancers that may be set, are uniformly packaged into an array of Advisor types and set to ProxyFactory. And the intensifier set by the AbstractAutoProxyCreator. interceptorNames property is in front.
  • customizeProxyFactory(proxyFactory); subclass can continue to process ProxyFactory
  • Set the Frozen property of ProxyFactory
  • setPreFiltered set preFiltered of ProxyFactory
  • getProxy returns the proxy object

We can see that there are two parts:

(1) create a proxyFactory factory and configure the factory

(2) proxyFactory.getProxy(getProxyClassLoader()) returns the proxy object.

proxyFactory.getProxy(ClassLoader classLoader)

proxyFactory.getProxy(ClassLoader classLoader) will first create a default policy factory, DefaultAopProxyFactory. DefaultAopProxyFactory will choose to create a JdkDynamicAopProxy object or a CglibAopProxy object according to whether the proxy is interface based or class based.

JdkDynamicAopProxy.getProxy() or cgliaopproxygetproxy() are the real return proxy objects.

Review again JDK dynamic agent and CGLIB dynamic agent

JdkDynamicAopProxy.getProxy()

@Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

We see two elements of JDK dynamic proxy: Proxy+InvocationHandler
The InvocationHandler is now the JdkDynamicAopProxy, which encapsulates the proposed. In this way, it perfectly connects the proposed and JDK dynamic agents.

The next step is to generate a bytecode JDK proxy class $Proxy66.class in memory to generate the real proxy object.

CglibAopProxygetProxy()
(omit some source codes and go straight to the topic)

            // Configure CGLIB Enhancer...
            Enhancer enhancer = createEnhancer();
            Callback[] callbacks = getCallbacks(rootClass);
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            return createProxyClassAndInstance(enhancer, callbacks);
        }
        

We see the familiar element Enhancer of Cglb. Where's MethodInterceptor?

Follow up on the getCallbacks() method, which creates a dynamicaadvisedinterceptor. The dynamic advised interceptor implements the MethodInterceptor and encapsulates the advisors.

Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

In this way, CGLB dynamic agent is perfectly combined with the proposed.
The next step is to generate a new bytecode CGLB proxy class in memory, and generate the real proxy object.

summary

The bottom layer of spring AOP is JDK dynamic agent and CGLB dynamic agent. By encapsulating the intensifier into the Advised, and connecting the Advised with the InvocationHandler or MethodInterceptor, AOP technology can be perfectly realized.

It's said that transactions are implemented according to AOP. Strike while the iron is hot. Let's see how transactions create agents. Coming soon
Welcome to pay attention to my public number [source code action], the latest personal understanding timely.

Keywords: Java Spring JDK

Added by kituscat on Wed, 23 Oct 2019 04:41:45 +0300