6. AOP Search Enhancer for Spring Source Analysis 6

Keywords: Programming Spring Attribute xml JDK

1. AOP use case of JDK dynamic proxy

public interface PersonService {
    void sayHello();
}

public class PersonServiceImpl implements PersonService{
    @Override
    public void sayHello() {
        System.out.println("lwh sayHello");
    }
}
@Aspect
public class Proxy1 {

    @Pointcut("execution(* *.sayHello(..))")
    public void sayHello(){}

    @Before("sayHello()")
    public void Proxy1Before(){
        System.out.println("Proxy1 before");
    }

    @After("sayHello()")
    public void Proxy1After(){
        System.out.println("Proxy1 after");
    }
}
@Aspect
public class Proxy2 {

    @Pointcut("execution(* *.sayHello(..))")
    public void sayHello(){}

    @Before("sayHello()")
    public void Proxy1Before(){
        System.out.println("Proxy2 before");
    }

    @Around("sayHello()")
    public Object Proxy2Around(ProceedingJoinPoint p){
        System.out.println("Proxy2 around before");

        Object o = null;

        try {
            o = p.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }

        System.out.println("Proxy2 around after");

        return o;
    }
}
<aop:aspectj-autoproxy/>

<bean id="personService" class="com.lwh.spring.JdkProxy.PersonServiceImpl"/>

<bean class="com.lwh.spring.JdkProxy.Proxy1"/>

<bean class="com.lwh.spring.JdkProxy.Proxy2"/>
public static void main(String[] args) {
    ApplicationContext ctx = new FileSystemXmlApplicationContext("jdkProxy.xml");

    PersonService personService = ctx.getBean("personService", PersonService.class);

    personService.sayHello();
}
Output:

2, from aop:aspect-autoproxy/ Start analysis

//When the label is configured in the Spring configuration file, Spring supports annotated AOP, searches the label globally and gets
public class AopNamespaceHandler extends NamespaceHandlerSupport {
   public void init() {
      //Delete some of the code, and when parsing the configuration file, encounter the tag, which is parsed using AspectJAuto ProxyBean Definition Parser
      registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
   }
}

if (delegate.isDefaultNamespace(ele)) {
//Resolve default tags, bean s tags for the second analysis, etc.
   parseDefaultElement(ele, delegate);
}
else {
//Resolve custom tags, such as aspectj-atuoproxy
   delegate.parseCustomElement(ele);
}

//This is where the final parsing will go.
public BeanDefinition parse(Element element, ParserContext parserContext) {

   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);

   extendBeanDefinition(element, parserContext);

   return null;
}
//Core logic
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                                                        ParserContext parserContext, Element sourceElement) {
   //1. Register or upgrade Annotation Aware Aspect JAuto Proxy Creator, which can automatically proxy phases based on the cut points defined by the @Point annotation
   //For simplicity, Spring uses a custom configuration to help us automatically register Annotation Aware Aspect JAuto Proxy Creator
   BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                                          parserContext.getRegistry(), parserContext.extractSource(sourceElement));
   //2. Processing proxy-target-class attributes and expose-proxy attributes
   useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
 
   //3. Register components and notify them for further processing by the listener
   registerComponentIfNecessary(beanDefinition, parserContext);
}
//Step 1: Register or upgrade Annotation Aware Aspect JAuto Proxy Creator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
      parserContext.getRegistry(), parserContext.extractSource(sourceElement));
public static BeanDefinition  registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, 
                                                                                   Object source) {
   return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {

   //AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator"
   //If an automatic agent creator already exists and the existing automatic agent is inconsistent with the current one, which one to use depends on priority
   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
         //The default priority, the return priority is actually its subscript position in APC_PRIORITY_LIST
         int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
          //The priorities Spring will create,
         int requiredPriority = findPriorityForClass(cls);
         if (currentPriority < requiredPriority) {
            //The most important thing to change a bean is to change its className attribute
            apcDefinition.setBeanClassName(cls.getName());
         }
      }
      return null;
   }
   RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
   beanDefinition.setSource(source);
   beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
   beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
   return beanDefinition;
}
private static final List<Class> APC_PRIORITY_LIST = new ArrayList<Class>();
static {
   APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
   APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
   //Annotation Aware Aspect JAuto ProxyCreator has the highest priority, you can see.
   APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
Step 2: Processing the proxy-target-class attribute and expose-proxy attribute
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
   if (sourceElement != null) {
       //Processing the proxy-target-class attribute, which enforces the use of CGLIB dynamic proxy, is actually setting an attribute to the bean Definition
      boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
      if (proxyTargetClass) {
         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
       //When dealing with expose-proxy attributes, sometimes SELF-calls within the target object will fail to implement aspect enhancement
      boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
      if (exposeProxy) {
         AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
   }
}
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
   }
}
Explain proxy-target-class and enforce CGLIB proxy:
1)<aop:config proxy-target-class=true />
2) When CGLIB proxy and @AspectJ automatic proxy support are needed, configure <aop: aspectj-autoproxy-proxy-traget-class=true/>

Explain expose-proxy. For example, this points to the target object, so calling this.b() will not execute the transaction aspect of b, so the transaction definition of B method will not be implemented.
Solution: Amend to ((Aservice) AopContext. CurrtProxy (). B ()
@Service
public class AServiceImpl implements AService{
    @Transactional(propagation = Propagation.REQUIRED)
    public void a() {
        this.b();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void b() {
    }
}

3. Creating Agents

Annotation Aware Aspect JAuto ProxyCreator implements BeanPostProcessor, so Spring instantiates it when loading bean s
 Before calling its postProcessAfterInitialization method, our AOP logic analysis also starts.
In the postProcessAfterInitialization of the parent AbstractAutoProxyCreator, the code is as follows:
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.containsKey(cacheKey)) {
         //If it's appropriate to be a proxy, you need to encapsulate the specified bean s
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   //If it has been processed, it returns directly
   if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
      return bean;
   }
   //No enhancement
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   //Whether a given bean represents an infrastructure class, the infrastructure class should not be proxyed, or the specified bean is configured without proxy
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   //Create a proxy if there are enhancements
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   //If enhancements are obtained, proxies need to be created for enhancements
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //Creating proxy objects
      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;
}
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
    //Includes two steps to capture all enhancements and find enhancements and applications for bean s in enhancements
   List advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
   //Step 1. Get the enhancer
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //Step 2. Find a matching enhancer
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}
//Step 1: Get the enhancer
protected List<Advisor> findCandidateAdvisors() {
   //When you configure AOP with annotations, you don't discard support for XML configuration. Here, you call the method of the parent class to load the AOP declaration in the configuration file.
   List<Advisor> advisors = super.findCandidateAdvisors();
   //Get the AOP declaration using annotation configuration
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}
//1. Get all beanName s, and in this step all beans registered in the beanFactory will be extracted.
//2. Traverse through all beanName s and find classes declaring @AspectJ annotations for further processing
//3. Extraction of enhancers for classes marked with @AspectJ annotations
//4. Put the results into the cache
public List<Advisor> buildAspectJAdvisors() {
   List<String> aspectNames = null;
   synchronized (this) {
      //Store beanName with @Aspect annotation
      aspectNames = this.aspectBeanNames;
      if (aspectNames == null) {
         List<Advisor> advisors = new LinkedList<Advisor>();
         aspectNames = new LinkedList<String>();
         //Get all beanName
         String[] beanNames = 
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
         //ergodic
         for (String beanName : beanNames) {
            //Unlawful skip, this method returns true by default, and rules are defined by subclass override
            if (!isEligibleBean(beanName)) {
               continue;
            }
            
            Class beanType = this.beanFactory.getType(beanName);
            if (beanType == null) {
               continue;
            }
            
            //If there are Aspect annotations
            if (this.advisorFactory.isAspect(beanType)) {
               //Put in cache
               aspectNames.add(beanName);
               AspectMetadata amd = new AspectMetadata(beanType, beanName);
               if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                  MetadataAwareAspectInstanceFactory factory =
                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                  //Core steps to resolve enhancements in tag @AspectJ annotation classes
                  List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                  
   //Partial code is omitted here
   return advisors;
}
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
   //class com.lwh.spring.JdkProxy.Proxy1
   final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();

   //com.lwh.spring.JdkProxy.Proxy1#0
   final String aspectName = maaif.getAspectMetadata().getAspectName();

   final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
         new LazySingletonAspectInstanceFactoryDecorator(maaif);

   final List<Advisor> advisors = new LinkedList<Advisor>();
   for (Method method : getAdvisorMethods(aspectClass)) {
      //a. Processing of ordinary intensifier
      //method is like public void com.lwh.spring.JdkProxy.Proxy1.Proxy1Before()
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }
   
   //b. Increase the synchronization instantiation enhancer
   if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
      advisors.add(0, instantiationAdvisor);
   }

   //c. Get the DeclareParents annotation, which is mainly used for introductions enhancement, and then reanalyze it later.
   for (Field field : aspectClass.getDeclaredFields()) {
      Advisor advisor = getDeclareParentsAdvisor(field);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   return advisors;
}
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
   final List<Method> methods = new LinkedList<Method>();
   //Method declared as Pointcut is not handled
   ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
      public void doWith(Method method) throws IllegalArgumentException {
         if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
            methods.add(method);
         }
      }
   });
   Collections.sort(methods, METHOD_COMPARATOR);
   return methods;
}
//Step a, Acquisition of General Enhancer
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
                                                        int declarationOrderInAspect, String aspectName) {
   //1. Acquisition of cut-point information, @org.aspectj.lang.annotation.Before(argNames=, value=sayHello())
   AspectJExpressionPointcut ajexp =
         getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
   if (ajexp == null) {
      return null;
   }
   //2. Generating enhancers based on tangent information
   return new InstantiationModelAwarePointcutAdvisorImpl(
         this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
   //Like @org.aspectj.lang.annotation.Before(argNames=, value=sayHello())
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   
   if (aspectJAnnotation == null) {
      return null;
   }

   AspectJExpressionPointcut ajexp =
         new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
   ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
   return ajexp;
}
//An enhancer is generated based on the tangent information, which is the implementation class of Advisor. It simply records the information in the class.
public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
               MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {

   this.declaredPointcut = ajexp;
   this.method = method;
   this.atAspectJAdvisorFactory = af;
   this.aspectInstanceFactory = aif;
   this.declarationOrder = declarationOrderInAspect;
   this.aspectName = aspectName;

   if (aif.getAspectMetadata().isLazilyInstantiated()) {
      Pointcut preInstantiationPointcut =
            Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
      this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
      this.lazy = true;
   }
   else {
      //Advisor=Advice+PointCut
      //Because different enhancements embody different logic, such as @Before and @After tags, the location of enhancer enhancements is different, so it needs to be real
      //Instantiate different enhancement logic
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
      this.pointcut = declaredPointcut;
      this.lazy = false;
   }
}
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    return this.atAspectJAdvisorFactory.getAdvice(
         this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
      MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
   //class com.lwh.spring.JdkProxy.Proxy1
   Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();

   //@org.aspectj.lang.annotation.Before(argNames=, value=sayHello())
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);

   //Delete part of the check code
   AbstractAspectJAdvice springAdvice;
   //Encapsulating different enhancers according to different annotation types
   switch (aspectJAnnotation.getAnnotationType()) {
      case AtBefore:
         springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
         break;
      case AtAfter:
         springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
         break;
      case AtAfterReturning:
         springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
         AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterReturningAnnotation.returning())) {
            springAdvice.setReturningName(afterReturningAnnotation.returning());
         }
         break;
      case AtAfterThrowing:
         springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
         AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
            springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
         }
         break;
      case AtAround:
         springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
         break;
      case AtPointcut:
         return null;
      default:
         throw new UnsupportedOperationException();
   }

   springAdvice.setAspectName(aspectName);
   springAdvice.setDeclarationOrder(declarationOrderInAspect);
   String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
   if (argNames != null) {
      springAdvice.setArgumentNamesFromStringArray(argNames);
   }
   springAdvice.calculateArgumentBindings();
   return springAdvice;
}
//Step 2: Get the matching enhancer
//1. Acquisition enhancer
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//2. Get the matching enhancers. In Step 1, all enhancers are not necessarily applicable to the current bean s. Choose the appropriate enhancers.
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
protected List<Advisor> findAdvisorsThatCanApply(
      List<Advisor> candidateAdvisors, Class beanClass, String beanName) {

   ProxyCreationContext.setCurrentProxiedBeanName(beanName);
   try {
      return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
   }
   finally {
      ProxyCreationContext.setCurrentProxiedBeanName(null);
   }
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
   
   List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
   for (Advisor candidate : candidateAdvisors) {
      //Processing introductions were enhanced and reanalysed later
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
         eligibleAdvisors.add(candidate);
      }
   }
   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
   for (Advisor candidate : candidateAdvisors) {
      //Introduction Enhancement has been processed
      if (candidate instanceof IntroductionAdvisor) {
         continue;
      }
      //Handling common enhancements
      if (canApply(candidate, clazz, hasIntroductions)) {
         eligibleAdvisors.add(candidate);
      }
   }
   return eligibleAdvisors;
}
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;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
   }
   else {
      return true;
   }
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
  
   if (!pc.getClassFilter().matches(targetClass)) {
      return false;
   }

   MethodMatcher methodMatcher = pc.getMethodMatcher();
   IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
   if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
   }

   Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
   classes.add(targetClass);
   for (Class<?> clazz : classes) {
      Method[] methods = clazz.getMethods();
      for (Method method : methods) {
         if ((introductionAwareMethodMatcher != null &&
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
               methodMatcher.matches(method, targetClass)) {
            return true;
         }
      }
   }

   return false;
}

Posted by realchamp on Thu, 10 Oct 2019 07:40:38 -0700