• Spring AOP 学习记录2


    主题

    补充学习一下一些关于Spring AOP的理解

    怎么找到当前Bean可以哪些advisor应用

    在当前Bean被AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization方法处理的过程中

    会需要找到当前Bean可以被应用到的所有advisor.

     

     看代码也知道大致的步骤:

    1.findCandidateAdvisors 找到所有的advisor

    2.findAdvisorsThatCanApply 过滤出可以使用的advisor

    findCandidateAdvisors

    如何找到所有advisor?

     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     }

    BF中取到所有的BeanName,并遍历.对于每个BeanName,找到对应的Type(Class).再去找它这个类是否有Aspect.class的注解

     从中我们可以看出我们如果要使用AOP有什么样的条件:

    1.这个Aspect的类应该要被当做Bean.不然BF中里没有这个Bean的定义.不管是在XML里直接定义BeanDifination还是@Component都OK.

    2.这个类应该要有@Aspect.不然怎么知道这个是一个aspect呢.(其实还有一种是compiledByAjc(clazz)..没用过.不清楚怎么用)

    找到了Aspect的类以后只要提取其中的advisor即可. List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

    大致核心如下:

    对于类的每个没有标记@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     }

    找到这个方法上的Aspect注解(@before..@after...@around...).从中提取出pointcut的表达式即可.

    Advisor = advise(你的asperct类里的方法) + pointcut(表达式)

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

    最后封装成InstantiationModelAwarePointcutAdvisorImpl并返回.

    findAdvisorsThatCanApply

    这里没有太多神奇的操作.

    找到所有的advisor以后就是判断每个advisor能不能作用到当前的类上. 怎么判断的? 之前是提出了pointcut的expression.那这里使用这个expr去和类比较一下即可.

     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委托MethodMatcher去做对应的match.

    具体调用比较复杂..我也没有仔细去看...

    AOP本类方法掉用本类方法不生效咋办

    这个问题可能经常会遇到.我相信80%出现的情况是一个操作数据库的service上的方法a上有@Transaction, 然后方法b是个很一般的方法.b会调用a.然后发现事务没生效.

    解决这个问题可能会有2种办法.

    expose-proxy

     <aop:aspectj-autoproxy expose-proxy="true"/>

    设置了这个标签属性以后可以在代码里

    AopContext.currentProxy()

    这样来获取当前类的proxy.然后调用方法就行了.

    原理呢? 其实很简单.以JDK动态代理为例

    JdkDynamicAopProxy.invoke 方法内部有一段代码

                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

    将当前bean对应的proxy设置到AopCOntext的currentProxy中.currentProxy是个threadLocal对象.完成绑定.

    所以在我们的bean中可以通过AopContext.currentProxy()获取之前设置进去的proxy对象.

    自身持有自身的引用

    class A {
    
      @Autowired
    
      private A a;
    
    }

    大致是这样的操作. 为什么这样可以呢? 首先应该要分析为什么本类调用本类方法AOP不起作用.

    ReflectiveMethodInvocation类中当interceptor的全部操作都已经做完以后会调用我们joinpoint的方法(我们的业务代码).

     

     注意这里传入的第一个参数是target. target不是proxy.是原始的你写的bean而不是AOP产生的代理对象.所以调用方法的时候会调用你本身对象的相关方法.

    因为不是proxy.也就不会再触发所有interceptor.自然也就没有了aop的advise的功能.

    那为什么本类注入本类的对象就可以呢?

    因为本类注入本类的时候, private A a中a是一个proxy..所以invokeJoinpoint的时候虽然this.target是原生的bean.但是他的field a仍然是proxy的a.所以调用a的相关方法仍然会经过interceptor处理,保留advise的功能.

    看到这里不知道大家会不会想到有一个循环引用的问题.

    https://www.cnblogs.com/abcwt112/p/12577109.html

    问题2种写到A中有B,B中有A的时候如果A被代理了.那会throw BeanCurrentlyInCreationException,因为B中注入的是原始的A,A后来又被代理返回了proxy.那B注入的原始的A就有问题,是原始的,而非代理的.

    那为啥这里A中注入A就没事呢?

    因为AnnotationAwareAspectJAutoProxyCreator这个类impl了org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference

     getEarlyBeanReference这个方法就是为了处理提前引用的.需要提前获取原始Bean引用的时候可以进行一些回调处理,这里的处理为需要提前获取引用的时候直接返回代理,而不是原始的Bean.

    当A Bean被加载,populate他的属性A a的时候,返回的并不是原生的A,而是proxy..接下来正在创建的原生A也会被替换成proxy返回.因此不会出现异常.

    Aspect的顺序

    多个aop如何处理顺序问题?

     找出bean可以被应用的advisor以后会排序,排序规则和BeanPostProcessor等都是一样的

    详见org.springframework.core.OrderComparator#doCompare

     1     private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
     2         boolean p1 = (o1 instanceof PriorityOrdered);
     3         boolean p2 = (o2 instanceof PriorityOrdered);
     4         if (p1 && !p2) {
     5             return -1;
     6         }
     7         else if (p2 && !p1) {
     8             return 1;
     9         }
    10 
    11         int i1 = getOrder(o1, sourceProvider);
    12         int i2 = getOrder(o2, sourceProvider);
    13         return Integer.compare(i1, i2);
    14     }
    15 
    16 
    17     private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
    18         Integer order = null;
    19         if (obj != null && sourceProvider != null) {
    20             Object orderSource = sourceProvider.getOrderSource(obj);
    21             if (orderSource != null) {
    22                 if (orderSource.getClass().isArray()) {
    23                     Object[] sources = ObjectUtils.toObjectArray(orderSource);
    24                     for (Object source : sources) {
    25                         order = findOrder(source);
    26                         if (order != null) {
    27                             break;
    28                         }
    29                     }
    30                 }
    31                 else {
    32                     order = findOrder(orderSource);
    33                 }
    34             }
    35         }
    36         return (order != null ? order : getOrder(obj));
    37     }
    38 
    39 
    40     protected int getOrder(@Nullable Object obj) {
    41         if (obj != null) {
    42             Integer order = findOrder(obj);
    43             if (order != null) {
    44                 return order;
    45             }
    46         }
    47         return Ordered.LOWEST_PRECEDENCE;
    48     }

    小结下就是:

    PriorityOrdered > getOrder > 没有order(Ordered.LOWEST_PRECEDENCE)

    数字越小越外层(越先有机会处理).

  • 相关阅读:
    【强转】QEMU+GDB调试linux内核全过程
    从上往下打印二叉树
    栈的压入、弹出序列
    包含main函数的栈
    顺时针打印矩阵
    Linux终端美化
    Linux 终端美化
    KDE桌面环境自带的Konsole终端配置
    KDE美化及常用设置
    KDE常用桌面插件总结
  • 原文地址:https://www.cnblogs.com/abcwt112/p/12681799.html
Copyright © 2020-2023  润新知