Spring AOP learning record 2

theme

Supplement to learn some understanding about Spring AOP

 

How to find which advisor applications the current bean can use

 

In the process of the current Bean being processed by the postProcessAfterInitialization method of AnnotationAwareAspectJAutoProxyCreator

 

 

You will need to find all advisors to which the current bean can be applied.

 

 

 

 Looking at the code also knows the approximate steps:

1.findCandidateAdvisors find all advisors

2.findAdvisorsThatCanApply filter out the available advisor

 

findCandidateAdvisors

How to find all advisors?

 1 /**
 2      * Look for AspectJ-annotated aspect beans in the current bean factory,
 3      * and return to a list of Spring AOP Advisors representing them.
 4      * <p>Creates a Spring Advisor for each AspectJ advice method.
 5      * @return the list of {@link org.springframework.aop.Advisor} beans
 6      * @see #isEligibleBean
 7      */
 8     public List<Advisor> buildAspectJAdvisors() {
 9         List<String> aspectNames = this.aspectBeanNames;
10 
11         if (aspectNames == null) {
12             synchronized (this) {
13                 aspectNames = this.aspectBeanNames;
14                 if (aspectNames == null) {
15                     List<Advisor> advisors = new ArrayList<>();
16                     aspectNames = new ArrayList<>();
17                     String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
18                             this.beanFactory, Object.class, true, false);
19                     for (String beanName : beanNames) {
20                         if (!isEligibleBean(beanName)) {
21                             continue;
22                         }
23                         // We must be careful not to instantiate beans eagerly as in this case they
24                         // would be cached by the Spring container but would not have been weaved.
25                         Class<?> beanType = this.beanFactory.getType(beanName);
26                         if (beanType == null) {
27                             continue;
28                         }
29                         if (this.advisorFactory.isAspect(beanType)) {
30                             aspectNames.add(beanName);
31                             AspectMetadata amd = new AspectMetadata(beanType, beanName);
32                             if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
33                                 MetadataAwareAspectInstanceFactory factory =
34                                         new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
35                                 List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
36                                 if (this.beanFactory.isSingleton(beanName)) {
37                                     this.advisorsCache.put(beanName, classAdvisors);
38                                 }
39                                 else {
40                                     this.aspectFactoryCache.put(beanName, factory);
41                                 }
42                                 advisors.addAll(classAdvisors);
43                             }
44                             else {
45                                 // Per target or per this.
46                                 if (this.beanFactory.isSingleton(beanName)) {
47                                     throw new IllegalArgumentException("Bean with name '" + beanName +
48                                             "' is a singleton, but aspect instantiation model is not singleton");
49                                 }
50                                 MetadataAwareAspectInstanceFactory factory =
51                                         new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
52                                 this.aspectFactoryCache.put(beanName, factory);
53                                 advisors.addAll(this.advisorFactory.getAdvisors(factory));
54                             }
55                         }
56                     }
57                     this.aspectBeanNames = aspectNames;
58                     return advisors;
59                 }
60             }
61         }
62 
63         if (aspectNames.isEmpty()) {
64             return Collections.emptyList();
65         }
66         List<Advisor> advisors = new ArrayList<>();
67         for (String aspectName : aspectNames) {
68             List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
69             if (cachedAdvisors != null) {
70                 advisors.addAll(cachedAdvisors);
71             }
72             else {
73                 MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
74                 advisors.addAll(this.advisorFactory.getAdvisors(factory));
75             }
76         }
77         return advisors;
78     }

Get all the BeanName in BF and traverse. For each BeanName, find the corresponding Type (Class). Then find out whether this class has the annotation of Aspect.class

 

 

 From this we can see what conditions we have if we want to use AOP:

1. This Aspect class should be regarded as a Bean. Otherwise, there is no definition of this Bean in BF. Whether it is directly defined in XML, BeanDifination or @Component are OK.

2. This class should have @Aspect. Otherwise, how do you know that this is an aspect. (In fact, there is another kind of compiledByAjc (clazz) .. Never used. Not sure how to use)

 

After finding the Aspect class, just extract the advisor. List <Advisor> classAdvisors = this.advisorFactory.getAdvisors (factory);

The core is roughly as follows:

For every method of class that is not marked @PointCut

1         for (Method method : getAdvisorMethods(aspectClass)) {
2             Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
3             if (advisor != null) {
4                 advisors.add(advisor);
5             }
6         }
 1     private List<Method> getAdvisorMethods(Class<?> aspectClass) {
 2         final List<Method> methods = new ArrayList<>();
 3         ReflectionUtils.doWithMethods(aspectClass, method -> {
 4             // Exclude pointcuts
 5             if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
 6                 methods.add(method);
 7             }
 8         }, ReflectionUtils.USER_DECLARED_METHODS);
 9         methods.sort(METHOD_COMPARATOR);
10         return methods;
11     }

 

Find the Aspect annotation (@before .. @ after ... @ around ...) on this method. Extract the pointcut expression from it.

Advisor = advise (method in your asperct class) + pointcut (expression)

 1     @Override
 2     @Nullable
 3     public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
 4             int declarationOrderInAspect, String aspectName) {
 5 
 6         validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
 7 
 8         AspectJExpressionPointcut expressionPointcut = getPointcut(
 9                 candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
10         if (expressionPointcut == null) {
11             return null;
12         }
13 
14         return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
15                 this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
16     }
 1     @Nullable
 2     private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
 3         AspectJAnnotation<?> aspectJAnnotation =
 4                 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
 5         if (aspectJAnnotation == null) {
 6             return null;
 7         }
 8 
 9         AspectJExpressionPointcut ajexp =
10                 new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
11         ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
12         if (this.beanFactory != null) {
13             ajexp.setBeanFactory(this.beanFactory);
14         }
15         return ajexp;
16     }
 1     /**
 2      * Find and return the first AspectJ annotation on the given method
 3      * (there <i>should</i> only be one anyway...).
 4      */
 5     @SuppressWarnings("unchecked")
 6     @Nullable
 7     protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
 8         for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
 9             AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
10             if (foundAnnotation != null) {
11                 return foundAnnotation;
12             }
13         }
14         return null;
15     }
1     private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
2             Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

Finally encapsulated into InstantiationModelAwarePointcutAdvisorImpl and returned.

 

findAdvisorsThatCanApply

There are not many magic operations here.

After finding all the advisors, it is judged whether each advisor can act on the current class. How to judge? The pointcut expression was proposed before. Then use this expr to compare it with the class.

 1     public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
 2         if (advisor instanceof IntroductionAdvisor) {
 3             return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
 4         }
 5         else if (advisor instanceof PointcutAdvisor) {
 6             PointcutAdvisor pca = (PointcutAdvisor) advisor;
 7             return canApply(pca.getPointcut(), targetClass, hasIntroductions);
 8         }
 9         else {
10             // It doesn't have a pointcut so we assume it applies.
11             return true;
12         }
13     }

Advisor commissions MethodMatcher to do the corresponding match.

The specific call is more complicated .. I did not look carefully ...

 

AOP methods of this type will not take effect if they are used.

This problem may be encountered frequently. I believe that 80% of the cases are @Transaction on method a of the service that operates the database, and then method b is a very general method. B will call a. Then I found that the transaction did not take effect.

 

Guess you like

Origin www.cnblogs.com/abcwt112/p/12681799.html