美文网首页springbootspring framework一些收藏
AOP-@EnableRetry实现的原理(源码存在瑕疵)

AOP-@EnableRetry实现的原理(源码存在瑕疵)

作者: 小胖学编程 | 来源:发表于2020-11-14 18:06 被阅读0次

    在使用Spring-Retry注解式重试时,需要在启动类上加上@EnableRetry注解。那么这个注解的作用是什么呢?

    启动类:

    @SpringBootApplication
    @EnableAspectJAutoProxy(exposeProxy=true)
    @EnableRetry
    @EnableConfigurationProperties
    @MapperScan("com.tellme.mapper")
    public class StandApplication {
        public static void main(String[] args) {
            SpringApplication.run(StandApplication.class, args);
        }
    }
    

    1. @EnableRetry源码分析

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @EnableAspectJAutoProxy(proxyTargetClass = false)
    @Import(RetryConfiguration.class)
    @Documented
    public @interface EnableRetry {
    
        /**
         * 指示是否创建基于子类(CGLIB)的代理,而不是标准的基于Java接口的代理。默认值是{@code false}。
         */
        boolean proxyTargetClass() default false;
    }
    

    该注解需要关注@Import(RetryConfiguration.class)

    1.1 @Import注解的作用

    目的:将RetryConfiguration加入到Spring容器。

    若启动类上持有@SpringBootApplication注解。那么会将启动类同包及其子包的SpringBean(例如:@Service等注解的bean)加入到Spring容器中。

    但是org.springframework.retry.annotation.RetryConfiguration类上虽然有@Configuration注解,但是Spring却扫描不到,所以需要使用@ImportRetryConfiguration类加入到本项目的Spring容器中。

    1.2 Spring AOP原理

    目的:创建Bean时,会判断bean是否满足RetryConfiguration的pointcut。若满足则创建代理对象。

    Spring AOP原理是动态代理,Spring在出初始化bean时,会在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization方法中执行配置的BeanPostProcessor生成代理对象。

    生成代理对象的源码分析:(测试代码见附录1)

    @Override 
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
    
        Object result = existingBean;
        for (BeanPostProcessor processor: getBeanPostProcessors()) {
            //使用BeanPostProcessor处理对象(生成代理对象)。
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }
    
    debug流程.png

    有上图可知,经过AnnotationAwareAspectJAutoProxyCreator后,目标对象成功变为了代理对象。

    SpringAOP联盟(7)-基础的自动代理(AnnotationAwareAspectJAutoProxyCreator)可知,AnnotationAwareAspectJAutoProxyCreator会获取到Spring容器中的所有的Advisor。在创建bean时,判断bean是否满足advisor持有的pointcut条件,若满足则创建代理对象。

    代理对象的advisor.png

    注:spring-retry使用AnnotationAwareAspectJAutoProxyCreator完成代理。

    1.3 带有瑕疵的RetryConfiguration类

    RetryConfiguration类实际上就是一个advisor。因为使用了@Import注解,那么该类会被加入到Spring容器。

    @Configuration
    public class RetryConfiguration extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware {
    
        private Advice advice;
    
        private Pointcut pointcut;
         ...
        private BeanFactory beanFactory;
    
        @PostConstruct
        public void init() {
            Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(1);
            retryableAnnotationTypes.add(Retryable.class);
            //获取切面
            this.pointcut = buildPointcut(retryableAnnotationTypes);
            //创建通知
            this.advice = buildAdvice();
            if (this.advice instanceof BeanFactoryAware) {
                ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
            }
        }
        ...
        protected Advice buildAdvice() {
            AnnotationAwareRetryOperationsInterceptor interceptor = new AnnotationAwareRetryOperationsInterceptor();
             ....
            return interceptor;
        }
    
        protected Pointcut buildPointcut(Set<Class<? extends Annotation>> retryAnnotationTypes) {
            ComposablePointcut result = null;
            for (Class<? extends Annotation> retryAnnotationType : retryAnnotationTypes) {
                //创建pointcut,切点中存在ClassFilter和MethodMatcher。
                Pointcut filter = new AnnotationClassOrMethodPointcut(retryAnnotationType);
                if (result == null) {
                    result = new ComposablePointcut(filter);
                }
                else {
                    //union(交集)和intersection(并集)的特性组合两个切入点
                    result.union(filter);
                }
            }
            return result;
        }
          //定义的切点
        private final class AnnotationClassOrMethodPointcut extends StaticMethodMatcherPointcut {
    
            private final MethodMatcher methodResolver;
    
            AnnotationClassOrMethodPointcut(Class<? extends Annotation> annotationType) {
                this.methodResolver = new AnnotationMethodMatcher(annotationType);
                setClassFilter(new AnnotationClassOrMethodFilter(annotationType));
            }
            //getClassFilter().matches(targetClass)只要类的任一方法(例如private)存在@Retry注解,那么便返回true。
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return getClassFilter().matches(targetClass) || this.methodResolver.matches(method, targetClass);
            }
            
            @Override
            public boolean equals(Object other) {
                if (this == other) {
                    return true;
                }
                if (!(other instanceof AnnotationClassOrMethodPointcut)) {
                    return false;
                }
                AnnotationClassOrMethodPointcut otherAdvisor = (AnnotationClassOrMethodPointcut) other;
                return ObjectUtils.nullSafeEquals(this.methodResolver, otherAdvisor.methodResolver);
            }
    
        }
        //类的过滤器
        private final class AnnotationClassOrMethodFilter extends AnnotationClassFilter {
    
            private final AnnotationMethodsResolver methodResolver;
    
            AnnotationClassOrMethodFilter(Class<? extends Annotation> annotationType) {
                super(annotationType, true);
                this.methodResolver = new AnnotationMethodsResolver(annotationType);
            }
            //super.matches(clazz)会判断注解是否在class上存在
           //this.methodResolver.hasAnnotatedMethods(clazz)若是注解在类的某一方法存在,返回true
            @Override
            public boolean matches(Class<?> clazz) {
                return super.matches(clazz) || this.methodResolver.hasAnnotatedMethods(clazz);
            }
    
        }
          //本类是AnnotationClassOrMethodFilter的一个属性
        private static class AnnotationMethodsResolver {
            //传入的注解
            private Class<? extends Annotation> annotationType;
            
            public AnnotationMethodsResolver(Class<? extends Annotation> annotationType) {
                this.annotationType = annotationType;
            }
            //判断传入的注解在该类上“所有方法”上是否存在,存在返回true
            public boolean hasAnnotatedMethods(Class<?> clazz) {
                final AtomicBoolean found = new AtomicBoolean(false);
                ReflectionUtils.doWithMethods(clazz,
                        new MethodCallback() {
                            @Override
                            public void doWith(Method method) throws IllegalArgumentException,
                                    IllegalAccessException {
                                if (found.get()) {
                                    return;
                                }
                                Annotation annotation = AnnotationUtils.findAnnotation(method,
                                        annotationType);
                                if (annotation != null) { found.set(true); }
                            }
                });
                return found.get();
            }
            
        }
        
    }
    

    advisor中会持有advice(通知)和pointcut(切点)。若bean中A方法满足pointcut,那么该bean会被代理。

    执行代理bean的A方法时,会调用advice(通知)进行增强。

    上面源码创建classFilter时,若类上无@Retryable注解,那么将通过反射获取到该类的所有method对象,判断method上是否存在@Retryable但是判断method是否满足methodMatcher时,难道不会再次遍历bean的所有method吗?

    1.3.1 瑕疵

    AnnotationClassOrMethodFilter是类过滤器,在AnnotationMethodsResolver却判断该类方法上是否存在对应注解,若存在,那么classFilter返回true。
    上文说到使用了AnnotationAwareAspectJAutoProxyCreator来完成代理,具体判断是否代理时依赖org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)方法,源码如下:

    public static boolean canApply(Pointcut pc, Class < ?>targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        //类过滤器(classFilter不是true,那么直接返回false)
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }
        //获取方法匹配器,若默认为ture,直接返回true
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) {
            // No need to iterate the methods if we're matching any method anyway...
            return true;
        }
    
        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }
    
        Set < Class < ?>>classes = new LinkedHashSet < >();
        if (!Proxy.isProxyClass(targetClass)) {
            classes.add(ClassUtils.getUserClass(targetClass));
        }
        classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        
        for (Class < ?>clazz: classes) {
            //解析类上所有的方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
          //遍历所有方法,若某个方法的methodMatcher返回true,那么直接返回true。
            for (Method method: methods) {
                if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }
    
        return false;
    }
    

    RetryConfiguration遍历所有方法,校验上面是否存在注解,来决定classFilter的状态。实际上canApply经过RetryConfiguration的classFilter后,依旧需要遍历所有方法,由methodMatcher.matches最终决定是否进行代理。

    所以:RetryConfiguration中去创建ClassFilter的操作是存在性能瑕疵的。

    将advisor类加入到Spring容器中,完成的AOP!

    附录

    1. 附录一

    测试代码:

    public interface XxService {
    
        void test();
    }
    
    @Service
    @Slf4j
    public class XxServiceImpl implements XxService {
    
        @Retryable
        @Override
        public void test() {
            log.info("test()");
            XxServiceImpl xxService = (XxServiceImpl) AopContext.currentProxy();
            xxService.t();
        }
    
    }
    
    断点的技巧.png

    相关文章

      网友评论

        本文标题:AOP-@EnableRetry实现的原理(源码存在瑕疵)

        本文链接:https://www.haomeiwen.com/subject/hopibktx.html