The article directories

      • Enable AOP automatic proxy and trigger timing
      • Gets the Bean matching Advisors chain
        • conclusion
      • Parsing Aspect implements Advice weaving in
        • JDK dynamic proxy
        • Cglib dynamic proxy
      • reference

This article is mainly for others to sort out the article, finally gives the reference link. Specific content according to my understanding, choice. Subsequent updates will be made with a deeper understanding of AOP

AOP relies on the IOC container for management.


Enable AOP automatic proxy and trigger timing

If you want Spring to support annotation mode AOP, you need to add the @EnableAspectJAutoProxy annotation on the startup class to match methods with all advisors in the IOC container. Annotations are defined as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass(a) default false;
    boolean exposeProxy(a) default false;
}
Copy the code

One of the more important annotation is @ Inport, it would inject AspectJAutoProxyRegistrar the ioc container. The source code definition is as follows:

/ / ImportBeanDefinitionRegistrar interface is realized
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		
        / / registered registerAspectJAnnotationAutoProxyCreator BeanDefinition
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
	
		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		
        // Inject the attribute values to the proxyTargetClass and exposeProxy attribute values of the @enableAspectJAutoProxy annotation
        / / registerAspectJAnnotationAutoProxyCreator BeanDefinition
        if(enableAspectJAutoProxy ! =null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}}Copy the code

AspectJAutoProxyRegistrar is mainly registerAspectJAnnotationAutoProxyCreator BeanDefinition registered to the ioc container.

RegisterAspectJAnnotationAutoProxyCreator class diagram as shown below:


AspectJAnnotationAutoProxyCreatorIt’s really just aBeanPostProcessor!

After creating the Spring Bean createBeanInstance and assembling the Property populateBean, we have a real implementation class. In initializeBean, the IoC container handles various callback events after the Bean is initialized, and then returns a final Bean object.

Including the BeanPostProcessor postProcessBeforeInitialization callback and postProcessAfterInitialization callback. While AspectJAnnotationAutoProxyCreator exactly is a BeanPostProcessor. It is easy to imagine that Spring AOP is doing proxy enhancement in this step!

The realization idea of proxy mode is :(interface) + real implementation class + proxy class. Only with real implementation class can we produce proxy class.

Can see from the source, postProcessBeforeInitialization callbacks and postProcessAfterInitialization callback is to rewrite the method in the AbstractAutoProxyCreator.

/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if(bean ! =null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

The postProcessBeforeInitialization method invocation chain is shown below:


PostProcessAfterInitialization will implement operations to create the proxy class, use configuration of interceptors to create a proxy class. First, take a look at the implementation of the wrapIfNecessary method:

/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    
    Advice, PointCut, Advisor, AopInfrastructureBean
    AOP parsing directly parses the aspect information and caches the aspect information
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // If advice is defined, create the proxy class
    // Returns all advisors, advice, and interceptors matching the current Bean
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if(specificInterceptors ! = DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // Create a proxy object
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

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

The call chain for the method is as follows:


When a class is a legitimate proppable class, it can be further wrapped with the wrapIfNecessary method, which returns either the proppable class or the implementation class itself. The core logic of the method has two steps (for scenarios where you need to generate proxy classes) :

  • Returns all advisors, advice, and interceptors that match the current Bean
  • The createProxy method is called to create the real proxy class object

Gets the Bean matching Advisors chain

Getting all advisors that match the current Bean is a two-step process:

  • As a candidate, get all advisors in the container, that is, wrap advice from all Aspect classes in the parse IOC container into Advisors
  • Filter the chain of Advisors from the candidate Advisors that match the current Bean

A shouldSkip implementation is AspectJAwareAdvisorAutoProxyCreator shouldSkip method, it first calls the findCandidateAdvisors for all candidate Advisors, Then determine if it is AspectJPointcutAdvisor or AspectJPointcutAdvisor based on the incoming beanClass and beanName.

public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

	@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 &&
					((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
				return true; }}return super.shouldSkip(beanClass, beanName); }}Copy the code

Matching the current Bean and return all the advisor, advice and interceptor invocation is getAdvicesAndAdvisorsForBean method and its source are defined as follows:

@Override
@Nullable
protectedObject[] getAdvicesAndAdvisorsForBean( Class<? > beanClass, String beanName,@Nullable TargetSource targetSource) {

    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

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;
}
Copy the code

It also calls the findCandidateAdvisors method to get all candidate Advisors. So, let’s see how it gets all the Advisors. The source code of this method is defined as follows:

public List<Advisor> findAdvisorBeans(a) {
    // Get the beanName of all advisors in the cache
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Advisor.class, true.false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    List<Advisor> advisors = new ArrayList<>();
    // Iterate through all advisors
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Skipping currently created advisor '" + name + "'"); }}else {
                try {
                    // If the corresponding Bean is already created, get the specified Bean from the BeanFactory and add it to the list of advisors
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if(bceBeanName ! =null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("Skipping advisor '" + name +
                                             "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            // Ignore: indicates a reference back to the bean we're trying to advise.
                            // We want to find advisors other than the currently created bean itself.
                            continue; }}throwex; }}}}// Returns all created Advisors
    return advisors;
}
Copy the code

And get through @ Aspect annotation Advisor, really call is BeanFactoryAspectJAdvisorsBuilder buildAspectJAdvisors method, the source code are defined as follows:

// Look for beans annotated by @Aspect in BeanFactory, create a Spring Advisor in the advice method of each Aspect class, and return the chain of Advisors

public List<Advisor> buildAspectJAdvisors(a) {
    List<String> aspectNames = this.aspectBeanNames;

    // The parsing aspect is triggered when the Singleton bean is instantiated for the first time, after which aspectNames are cached
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                // Used to hold all parsed Advisors collection objects
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                // Fetch the names of all Spring beans
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Object.class, true.false);
                // iterate over the judgment
                for (String beanName : beanNames) {
                    if(! isEligibleBean(beanName)) {continue;
                    }
                    // Get the corresponding Class object from the ioc container via beanNameClass<? > beanType =this.beanFactory.getType(beanName, false);
                    if (beanType == null) {
                        continue;
                    }
                    // Do you want to use @aspect annotations?
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            // Parse all advisors from the Advice class
                            // A Spring Advisor is created for the advice method of each Aspect class
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            // Add to the cache
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        }
                        else {
                            // Per target or per this.
                            if (this.beanFactory.isSingleton(beanName)) {
                                throw new IllegalArgumentException("Bean with name '" + beanName +
                                                                   "' is a singleton, but aspect instantiation model is not singleton");
                            }
                            MetadataAwareAspectInstanceFactory factory =
                                new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                            this.aspectFactoryCache.put(beanName, factory);
                            advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}}this.aspectBeanNames = aspectNames;
                returnadvisors; }}}if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if(cachedAdvisors ! =null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}return advisors;
}
Copy the code

In order to avoid fetching all beanName every time, parsing judgment, introduced the mechanism of caching; Also, the Aspect class is judged based on whether the Spring Bean is decorated by the @aspect annotation.

Let’s look at the core method getAdvisors above:

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // Get the class marked as AspectClass<? > aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();// Get the name of the facet class
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    // Check the section class
    validate(aspectClass);

    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
        new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new ArrayList<>();
    // Get all the methods in the section class except the @pointcut annotation
    for (Method method : getAdvisorMethods(aspectClass)) {
       	// Loop through the methods in the section to get the Advisor for the current method
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
        if(advisor ! =null) {
            // Save the obtained Advisoradvisors.add(advisor); }}// If the Advisor is not empty and deferred initialization is configured, add synchronous instantiation Advisor in the first place
    if(! advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor =new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if(advisor ! =null) { advisors.add(advisor); }}return advisors;
}
Copy the code

Then look at the getAdvisor method above:

@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
                          int declarationOrderInAspect, String aspectName) {
	/ / inspection
    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
	// Get the PointCut for the current method
    AspectJExpressionPointcut expressionPointcut = getPointcut(
        candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }
	// Generate advisors from PointCut
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                                                          this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
Copy the code

Object type is InstantiationModelAwarePointcutAdvisorImpl Advisor, call the constructor is as follows:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
                                                  Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
                                                  MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
	
    // Pointcut expression
    this.declaredPointcut = declaredPointcut;
    // The Class object of the plane
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    // The name of the section method
    this.methodName = aspectJAdviceMethod.getName();
    // The parameter type of the section method
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    // The section method is exclusive
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    // aspectJ's Advisor factory
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    // aspectJ instance factory
    this.aspectInstanceFactory = aspectInstanceFactory;
    // The order of the cuts
    this.declarationOrder = declarationOrder;
    // The name of the section
    this.aspectName = aspectName;
	// If lazy initialization is configured
    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        // Static part of the pointcut is a lazy type.
        Pointcut preInstantiationPointcut = Pointcuts.union(
            aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

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

The Advisor is initialized with the instantiateAdvice method:

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
                                                         this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    return(advice ! =null ? advice : EMPTY_ADVICE);
}
Copy the code

The getAdvice method is called:

@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
                        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
	
    // Get the section classClass<? > candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass);// Get the annotation on the methodAspectJAnnotation<? > aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {
        return null;
    }

    // Check if it is a class annotated by @aspect
    if(! isAspect(candidateAspectClass)) {throw new AopConfigException("Advice must be declared inside an aspect type: " +
                                     "Offending method '" + candidateAdviceMethod + "' in class [" +
                                     candidateAspectClass.getName() + "]");
    }

    if (logger.isDebugEnabled()) {
        logger.debug("Found AspectJ method: " + candidateAdviceMethod);
    }

    AbstractAspectJAdvice springAdvice;
	// Determine the type of advice annotation annotated on the method
    switch (aspectJAnnotation.getAnnotationType()) {
        case AtPointcut:
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        case AtAround:
            springAdvice = new AspectJAroundAdvice(
                candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtBefore:
            springAdvice = new AspectJMethodBeforeAdvice(
                candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfter:
            springAdvice = new AspectJAfterAdvice(
                candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfterReturning:
            springAdvice = new AspectJAfterReturningAdvice(
                candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;
        case AtAfterThrowing:
            springAdvice = new AspectJAfterThrowingAdvice(
                candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
        default:
            throw new UnsupportedOperationException(
                "Unsupported advice type on method: " + candidateAdviceMethod);
    }

    // Now to configure the advice...
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if(argNames ! =null) {
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();

    return springAdvice;
}
Copy the code

At this point, we get all the advice in the class annotated by the @aspect annotation and generate the Advisor based on the type of annotation on the advice method. The Advisors at this point contain candidate Advisors, and then you need to filter the chain of Advisors that match the current Bean.

protected List<Advisor> findEligibleAdvisors(Class
        beanClass, String beanName) {
    // Get candidate Advisors
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // Filter out the Advisors chain that matches the current Bean
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if(! eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); }return eligibleAdvisors;
}
Copy the code

The implementation of the findAdvisorsThatCanApply method above looks like this:

protected List<Advisor> findAdvisorsThatCanApply( List
       
         candidateAdvisors, Class
         beanClass, String beanName)
        {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null); }}public static List<Advisor> findAdvisorsThatCanApply(List
       
         candidateAdvisors, Class
         clazz)
        {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    for (Advisor candidate : candidateAdvisors) {
        // Get the Advisor of the IntroductionAdvisor type
        if (candidate instanceofIntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); }}booleanhasIntroductions = ! eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // The key method of filtering
        if(canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); }}return eligibleAdvisors;
}
Copy the code

The key filtering method canApply is implemented as follows:

public static boolean canApply(Advisor advisor, Class<? > targetClass,boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // Determine whether the Advisor's PointCut can be applied to a method in the class
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // It doesn't have a pointcut so we assume it applies.
        return true; }}Copy the code
public static boolean canApply(Pointcut pc, Class<? > targetClass,boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // Use calssfilter to match class
    if(! pc.getClassFilter().matches(targetClass)) {return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        // If an arbitrary method is matched, no iteration is required
        return true;
    }

    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
	
    // Find all interfaces implemented by the current class and its ancestor classSet<Class<? >> classes =new LinkedHashSet<>();
    if(! Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));// Get all the implementation interfaces and iterate through the public methods that match the interfaces
    for(Class<? > clazz : classes) {// Get the list of methods for the current class
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // Matches the method with methodMatcher and returns immediately upon success
            if(introductionAwareMethodMatcher ! =null ?
                introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                methodMatcher.matches(method, targetClass)) {
                return true; }}}return false;
}
Copy the code

Mainly completed by ClassFilter and MethodMatcher screening work, such as AspectJExpressionPointcut realized ClassFilter and MethodMatcher interface, finally by the AspectJ expression parsing. After finding the chain of Advisors, the sortAdvisors method is called to sort, and the final result is the chain of Advisors used by the target class.

conclusion

The core logic of how Spring AOP gets the corresponding Bean adaptor’s Advisors chain is as follows:

  • Gets all Aspect classes in the current IOC container

  • Create a Spring Advisor for the advice method of each Aspect class, which can be subdivided into

    • Iterate through all advice methods
    • Parsing method annotations and pointcuts
    • Instantiate the Advisor object
  • Candidate Advisors are obtained and cached for direct retrieval next time

  • The Advisor matching the target class is screened from the candidate Advisors

    • Gets the pointcut to the Advisor, pointcut
    • Gets all public methods of the current Target class
    • The traversal method matches the current method with the pointcut’s methodMatcher, and as long as one match is successful, it is equivalent to the current Advisor adaptation
  • Sort the filtered chain of Advisors

  • The end of the


Parsing Aspect implements Advice weaving in

Now that we have an Advisors chain that matches the current Bean, we need to create a proxy class object by calling the createProxy method.

protected Object createProxy(Class<? > beanClass,@Nullable String beanName,
                             @Nullable Object[] specificInterceptors, TargetSource targetSource) {
	
    / / if the bean factory for ConfigurableListableBeanFactory
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
	/ / create ProxyBeanfactory
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
	
    // If proxyTargetClass is set to true, Cglib is used to generate the proxy class
    // Otherwise, judge according to the circumstances
    if(! proxyFactory.isProxyTargetClass()) {// Generate the proxy class according to the interface or class
        if (shouldProxyTargetClass(beanClass, beanName)) {
            // Generate the proxy class from the class, using the Cglib proxy
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            // Generate proxy classes based on the interfaceevaluateProxyInterfaces(beanClass, proxyFactory); }}// Class all Advisors array
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    / / in Advisor
    proxyFactory.addAdvisors(advisors);
    // Set the class to proxy
    proxyFactory.setTargetSource(targetSource);
    // Customize the proxy
    customizeProxyFactory(proxyFactory);
	// The default value is false. The configuration of the proxy cannot be modified after the proxy is configured
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
	// Generate proxy classes based on the various configurations of the factory
    return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code

The basic process is as follows:

  • Gets an attribute in the current class
  • Adding a Proxy Interface
  • Encapsulate the Advisor and add toProxyFactoryIn the
  • Sets the class to proide
  • Spring provides custom functions for subclassescustomizeProxyFactory, subclasses can pair in this functionProxyFactoryFurther encapsulation of
  • Get agent operation

The getProxy method is implemented as follows: first create an AopProxy, and then get the proxy class:

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

The source code for creating AopProxy is as follows:

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

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // Determine whether to use JDK proxy or Cglib proxy
    // If proxyTargetClass is true, use Cglib to generate subclasses of the target class as the proxy class
    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 the interface is implemented, use the JDK dynamic proxy to generate proxy classes
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
           	// The JDK proxy lives the proxy class
            return new JdkDynamicAopProxy(config);
        }
        // Otherwise, use Cglib to generate the proxy class
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // If none of the above conditions are met, use JDK dynamic proxy
        return newJdkDynamicAopProxy(config); }}Copy the code

After this step, we obtained the corresponding implementation classes of AopProxy according to ProxyConfig, namely JdkDynamicAopProxy and ObjenesisCglibAopProxy respectively.

JDK dynamic proxy

JdkDynamicAopProxy is defined as follows:

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
    Assert.notNull(config, "AdvisedSupport must not be null");
    if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
        throw new AopConfigException("No advisors and no TargetSource specified");
    }
    this.advised = config;
}
Copy the code

Its getProxy method is implemented as follows:

final class JdkDynamicAopProxy implements AopProxy.InvocationHandler.Serializable {
    @Override
    public Object getProxy(a) {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        // Get the completed set of interfaces for the proxyClass<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        // Find any equals or hashCode methods that might be defined on the provided set of interfaces
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }}Copy the code

Finally, the newProxyInstance method of Proxy class is called to generate Proxy class object. In JDK dynamic proxy, the invoke(Object Proxy, Method Method, Object[] args) Method is used to invoke the Method of the target class and perform some enhanced operations that the proxy class wants to do. In Spring AOP, the Invoke method also wraps the AOP woven implementation.

The invoke method is implemented as follows:

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

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

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // If the current method is equals and the method is not defined in the interface, the equals method is automatically generated
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // If the current method is a hashCode method and the method is not defined in the interface, the hashCode method is automatically generated
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
		// Sometimes the self-invocation inside the target class object cannot achieve the aspect enhancement, and the proxy needs to be exposed through properties
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

		// Get the target class
        target = targetSource.getTarget();
        // Get the Class object of the target ClassClass<? > targetClass = (target ! =null ? target.getClass() : null);

        // Get the method's interceptor chain, namely the Advisors chain
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

       
        if (chain.isEmpty()) {
            // If there is no interceptor, the corresponding method of the target class is called directly
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // Otherwise, call the interceptor first, and then call the target class method
            MethodInvocation invocation =
                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Responsibility chain mode, along the chain of Advisors
            retVal = invocation.proceed();
        }

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

The core of the invoke method logic is to use the chain of responsibility pattern to recursively invoke methods to accomplish the weaving of advice.

Understand the chain of responsibility pattern in the design pattern

Therefore, the key implementation logic is in the PROCEED method, which implements the following:

@Override
@Nullable
public Object proceed(a) throws Throwable {
    // Recursive operation, starting from the interceptor with index -1 and increasing in order
    // When the Advisors in the Advisors chain are all iterated, the corresponding methods in the target class are called, which is realized by reflection
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
	// Get the next Advisor to execute
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceofInterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<? > targetClass = (this.targetClass ! =null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            / / call Advisor
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            returnproceed(); }}else {
        // If PointCut is set to match a specific method, it is called without matching the method
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code

Let’s take a look at ((MethodInterceptor) interceptorOrInterceptionAdvice.) invoke (this), that there are many methods to realize, some of which we are familiar with or need to pay attention to the implementation of the corresponding type is our Advice, Or the timing of the enhancement. The corresponding ones are Before, After, after-returning, after-throwing and Around.

Cglib dynamic proxy

The Cglib agent is similar to the JDK agent in terms of flow, but differs in implementation. The core is the Enhancer and the process of getting callbacks. The getProxy method of ObjenesisCglibAopProxy is implemented as follows:

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
	if (logger.isTraceEnabled()) {
		logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
	}

	try {
           // Get the parent Class object, i.e. the target Class objectClass<? > rootClass =this.advised.getTargetClass(); Assert.state(rootClass ! =null."Target class must be available for creating a CGLIB proxy"); Class<? > proxySuperClass = rootClass;if(rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { proxySuperClass = rootClass.getSuperclass(); Class<? >[] additionalInterfaces = rootClass.getInterfaces();for(Class<? > additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface); }}// Validate the class, writing log messages as necessary.
		validateClassIfNecessary(proxySuperClass, classLoader);

		// create an Enhancer for Cglib
		Enhancer enhancer = createEnhancer();
		if(classLoader ! =null) {
			enhancer.setClassLoader(classLoader);
			if (classLoader instanceof SmartClassLoader &&
					((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
				enhancer.setUseCache(false); }}// Set the parent class
		enhancer.setSuperclass(proxySuperClass);
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(newClassLoaderAwareGeneratorStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass); Class<? >[] types =newClass<? >[callbacks.length];for (int x = 0; x < types.length; x++) {
			types[x] = callbacks[x].getClass();
		}
		// Set the callback
		enhancer.setCallbackFilter(new ProxyCallbackFilter(
				this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
		enhancer.setCallbackTypes(types);

		// Create and return the proxy class instance
		return createProxyClassAndInstance(enhancer, callbacks);
	}
	catch (CodeGenerationException | IllegalArgumentException ex) {
		throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
				": Common causes of this problem include using a final class or a non-visible class",
				ex);
	}
	catch (Throwable ex) {
		// TargetSource.getTarget() failed
		throw new AopConfigException("Unexpected AOP exception", ex); }}Copy the code

At this point, the aspect oriented process of the Spring AOP implementation is resolved, and when the corresponding proxy class is returned, the bean obtained by subsequent programs is the proxy class object, not the target class object. For the target class method of the generation call, will be proxy to the proxy class object, before and after the method execution of the enhancement operation weaving.

The choice between JDK proxy and Cglib proxy is based on the following principles: if it is a singleton, it is best to use Cglib proxy; if it is a multi-instance, it is best to use JDK proxy.

The JDK performs better when creating proxy objects than CGLib proxies, while generating proxy objects performs worse than CGLib proxies.


reference

Spring AOP source code parsing Spring AOP – annotated way to use the introduction (long in detail) to you graphically illustrated Spring AOP source code (2) to you vividly explain Spring AOP source code (3) Spring AOP source code parsing