在使用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 {
/**
* 指示是否創(chuàng)建基于子類(CGLIB)的代理,而不是標(biāo)準(zhǔn)的基于Java接口的代理。默認(rèn)值是{@code false}。
*/
boolean proxyTargetClass() default false;
}
該注解需要關(guān)注@Import(RetryConfiguration.class)。
1.1 @Import注解的作用
目的:將RetryConfiguration加入到Spring容器。
若啟動類上持有@SpringBootApplication注解。那么會將啟動類同包及其子包的SpringBean(例如:@Service等注解的bean)加入到Spring容器中。
但是org.springframework.retry.annotation.RetryConfiguration類上雖然有@Configuration注解,但是Spring卻掃描不到,所以需要使用@Import將RetryConfiguration類加入到本項(xiàng)目的Spring容器中。
1.2 Spring AOP原理
目的:創(chuàng)建Bean時,會判斷bean是否滿足RetryConfiguration的pointcut。若滿足則創(chuàng)建代理對象。
Spring AOP原理是動態(tài)代理,Spring在出初始化bean時,會在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization方法中執(zhí)行配置的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;
}

有上圖可知,經(jīng)過AnnotationAwareAspectJAutoProxyCreator后,目標(biāo)對象成功變?yōu)榱舜韺ο蟆?/p>
SpringAOP聯(lián)盟(7)-基礎(chǔ)的自動代理(AnnotationAwareAspectJAutoProxyCreator)可知,AnnotationAwareAspectJAutoProxyCreator會獲取到Spring容器中的所有的Advisor。在創(chuàng)建bean時,判斷bean是否滿足advisor持有的pointcut條件,若滿足則創(chuàng)建代理對象。

注:spring-retry使用AnnotationAwareAspectJAutoProxyCreator完成代理。
1.3 帶有瑕疵的RetryConfiguration類
RetryConfiguration類實(shí)際上就是一個advisor。因?yàn)槭褂昧?code>@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);
//創(chuàng)建通知
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) {
//創(chuàng)建pointcut,切點(diǎn)中存在ClassFilter和MethodMatcher。
Pointcut filter = new AnnotationClassOrMethodPointcut(retryAnnotationType);
if (result == null) {
result = new ComposablePointcut(filter);
}
else {
//union(交集)和intersection(并集)的特性組合兩個切入點(diǎn)
result.union(filter);
}
}
return result;
}
//定義的切點(diǎn)
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(切點(diǎn))。若bean中A方法滿足pointcut,那么該bean會被代理。
執(zhí)行代理bean的A方法時,會調(diào)用advice(通知)進(jìn)行增強(qiáng)。
上面源碼創(chuàng)建
classFilter時,若類上無@Retryable注解,那么將通過反射獲取到該類的所有method對象,判斷method上是否存在@Retryable。但是判斷method是否滿足methodMatcher時,難道不會再次遍歷bean的所有method嗎?
1.3.1 瑕疵
AnnotationClassOrMethodFilter是類過濾器,在AnnotationMethodsResolver卻判斷該類方法上是否存在對應(yīng)注解,若存在,那么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;
}
//獲取方法匹配器,若默認(rèn)為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遍歷所有方法,校驗(yàn)上面是否存在注解,來決定classFilter的狀態(tài)。實(shí)際上canApply經(jīng)過RetryConfiguration的classFilter后,依舊需要遍歷所有方法,由methodMatcher.matches最終決定是否進(jìn)行代理。
所以:RetryConfiguration中去創(chuàng)建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();
}
}
