springbootspring framework一些收藏

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

2020-11-14  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读