上一篇文章,我們快速介紹了下spring-retry的使用技巧,本篇我們將會剖析源碼去學(xué)習(xí)
一、 EnableRetry注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
@Documented
public @interface EnableRetry {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
* standard Java interface-based proxies. The default is {@code false}.
* @return whether to proxy or not to proxy the class
*/
@AliasFor(annotation = EnableAspectJAutoProxy.class)
boolean proxyTargetClass() default false;
/**
* Indicate the order in which the {@link RetryConfiguration} AOP <b>advice</b> should
* be applied.
* <p>
* The default is {@code Ordered.LOWEST_PRECEDENCE - 1} in order to make sure the
* advice is applied before other advices with {@link Ordered#LOWEST_PRECEDENCE} order
* (e.g. an advice responsible for {@code @Transactional} behavior).
*/
int order() default Ordered.LOWEST_PRECEDENCE - 1;
}
英文翻譯我就不再解釋了,上面說的很清楚;這里重點(diǎn)提一下@Import(RetryConfiguration.class)這個(gè)注解,表明了@EnableRetry注解的啟動配置類是RetryConfiguration, 通過@Import注解來注入對應(yīng)的配置類,這樣的做法同樣可見于@EnableAsync/@EnableScheduling等注解上; 看到這里,如何定義一個(gè)Enable配置注解是不是會了呢~
二、 RetryConfiguration如何構(gòu)建 ponintCut和advisor
@SuppressWarnings("serial")
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Component
public class RetryConfiguration extends AbstractPointcutAdvisor
implements IntroductionAdvisor, BeanFactoryAware, InitializingBean, SmartInitializingSingleton, ImportAware {
//在這里構(gòu)建了ponintCut和advice
@Override
public void afterPropertiesSet() throws Exception {
this.retryContextCache = findBean(RetryContextCache.class);
this.methodArgumentsKeyGenerator = findBean(MethodArgumentsKeyGenerator.class);
this.newMethodArgumentsIdentifier = findBean(NewMethodArgumentsIdentifier.class);
this.sleeper = findBean(Sleeper.class);
Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<>(1);
retryableAnnotationTypes.add(Retryable.class);
//這里構(gòu)建基于@Retryable注解的切點(diǎn)
this.pointcut = buildPointcut(retryableAnnotationTypes);
//這里構(gòu)建了aop的通知
this.advice = buildAdvice();
this.advice.setBeanFactory(this.beanFactory);
if (this.enableRetry != null) {
setOrder(enableRetry.getNumber("order"));
}
}
}
RetryConfiguration繼承AbstractPointcutAdvisor實(shí)現(xiàn)了一個(gè)環(huán)繞切面,通知的邏輯見AnnotationAwareRetryOperationsInterceptor的實(shí)現(xiàn); 這里需要補(bǔ)一下spring AOP的基礎(chǔ)知識,詳情見文檔 https://cloud.tencent.com/developer/article/1808649 ,我覺的這篇文章已經(jīng)寫的非常好了,就不再細(xì)述;
接下來我們重點(diǎn)關(guān)注 AnnotationAwareRetryOperationsInterceptor 的實(shí)現(xiàn)邏輯,這里才是retgry啟動業(yè)務(wù)的實(shí)現(xiàn)邏輯;
三、AnotationAwareRetryOperationsInterceptor 重試retry邏輯的裝配
我們看下它的核心實(shí)現(xiàn)
public class AnnotationAwareRetryOperationsInterceptor implements IntroductionInterceptor, BeanFactoryAware {
//...省略其它代碼
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//這里獲取代理MethodInterceptor
MethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());
if (delegate != null) {
return delegate.invoke(invocation);
}
else {
return invocation.proceed();
}
}
/**
* 拿到代理類的實(shí)現(xiàn)
**
private MethodInterceptor getDelegate(Object target, Method method) {
//緩存解析結(jié)果,主要用于class級別的retry配置
ConcurrentMap<Method, MethodInterceptor> cachedMethods = this.delegates.get(target);
if (cachedMethods == null) {
cachedMethods = new ConcurrentHashMap<>();
}
MethodInterceptor delegate = cachedMethods.get(method);
if (delegate == null) {
MethodInterceptor interceptor = NULL_INTERCEPTOR;
Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);
if (retryable == null) {
retryable = classLevelAnnotation(method, Retryable.class);
}
if (retryable == null) {
retryable = findAnnotationOnTarget(target, method, Retryable.class);
}
if (retryable != null) {
//假如你需要實(shí)現(xiàn)自定義的攔截器,那就走的是這里的邏輯,例如你要實(shí)現(xiàn)retry的上下文放置到數(shù)據(jù)庫里記錄,不放在內(nèi)存里,那你就要考慮這里作文章了
if (StringUtils.hasText(retryable.interceptor())) {
interceptor = this.beanFactory.getBean(retryable.interceptor(), MethodInterceptor.class);
}
//有狀態(tài)的retry, 走的是這里,我沒有深入研究stateful的應(yīng)用場景,總感覺即便是有狀態(tài)的重試,必然跟業(yè)務(wù)邏輯本身也強(qiáng)關(guān)聯(lián)的,那狀態(tài)保護(hù)的邏輯,肯定也會抽出來,所以我不考慮放到框架本身去做,畢竟不同業(yè)務(wù),狀態(tài)保護(hù)方法不同;
else if (retryable.stateful()) {
interceptor = getStatefulInterceptor(target, method, retryable);
}
//這里應(yīng)該是通用邏輯鏈路了
else {
interceptor = getStatelessInterceptor(target, method, retryable);
}
}
cachedMethods.putIfAbsent(method, interceptor);
delegate = cachedMethods.get(method);
}
this.delegates.putIfAbsent(target, cachedMethods);
return delegate == NULL_INTERCEPTOR ? null : delegate;
}
//無狀態(tài)的重試攔截器
private MethodInterceptor getStatelessInterceptor(Object target, Method method, Retryable retryable) {
//這里基于注解生產(chǎn)重試策略 和 延遲策略
RetryTemplate template = createTemplate(retryable.listeners());
template.setRetryPolicy(getRetryPolicy(retryable, true));
template.setBackOffPolicy(getBackoffPolicy(retryable.backoff(), true));
return RetryInterceptorBuilder.stateless()
.retryOperations(template)
.label(retryable.label())
//這里關(guān)注下怎么構(gòu)建recover的
.recoverer(getRecoverer(target, method))
.build();
}
//...省略其它代碼
}
最終,無狀態(tài)的重試攔截器見,所以RetryOperationsInterceptor是常用鏈路的實(shí)現(xiàn)邏輯
public static class StatelessRetryInterceptorBuilder extends RetryInterceptorBuilder<RetryOperationsInterceptor> {
private final RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();
@Override
public RetryOperationsInterceptor build() {
if (this.recoverer != null) {
this.interceptor.setRecoverer(this.recoverer);
}
if (this.retryOperations != null) {
this.interceptor.setRetryOperations(this.retryOperations);
}
else {
this.interceptor.setRetryOperations(this.retryTemplate);
}
if (this.label != null) {
this.interceptor.setLabel(this.label);
}
return this.interceptor;
}
private StatelessRetryInterceptorBuilder() {
}
}
四、RetryOperationsInterceptor 常用retry operation的實(shí)現(xiàn)邏輯
RetryOperationsInterceptor是直接重試攔截器的實(shí)現(xiàn)邏輯,它的邏輯比較簡單,封裝了retryTemplate的執(zhí)行邏輯;核心代碼見RetryOperationsInterceptor#invoke()文章來源:http://www.zghlxwxcb.cn/news/detail-685176.html
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
//這個(gè)名字主要用于構(gòu)建上下文的時(shí)候用,正常有么有意義不大
String name;
if (StringUtils.hasText(this.label)) {
name = this.label;
}
else {
name = invocation.getMethod().toGenericString();
}
final String label = name;
//重試回調(diào),這里不作贅述
RetryCallback<Object, Throwable> retryCallback = new MethodInvocationRetryCallback<Object, Throwable>(
invocation, label) {
@Override
public Object doWithRetry(RetryContext context) throws Exception {
context.setAttribute(RetryContext.NAME, this.label);
context.setAttribute("ARGS", new Args(invocation.getArguments()));
/*
* If we don't copy the invocation carefully it won't keep a reference to
* the other interceptors in the chain. We don't have a choice here but to
* specialise to ReflectiveMethodInvocation (but how often would another
* implementation come along?).
*/
if (this.invocation instanceof ProxyMethodInvocation) {
context.setAttribute("___proxy___", ((ProxyMethodInvocation) this.invocation).getProxy());
try {
return ((ProxyMethodInvocation) this.invocation).invocableClone().proceed();
}
catch (Exception | Error e) {
throw e;
}
catch (Throwable e) {
throw new IllegalStateException(e);
}
}
else {
throw new IllegalStateException(
"MethodInvocation of the wrong type detected - this should not happen with Spring AOP, "
+ "so please raise an issue if you see this exception");
}
}
};
//有recover的時(shí)候,走這里就好
if (this.recoverer != null) {
ItemRecovererCallback recoveryCallback = new ItemRecovererCallback(invocation.getArguments(),
this.recoverer);
try {
Object recovered = this.retryOperations.execute(retryCallback, recoveryCallback);
return recovered;
}
finally {
RetryContext context = RetrySynchronizationManager.getContext();
if (context != null) {
context.removeAttribute("__proxy__");
}
}
}
//沒有recover走這里,都是調(diào)用RetryTemplate#execute的邏輯
return this.retryOperations.execute(retryCallback);
}
至此,全部的裝配邏輯就講結(jié)束,流程看下來并不復(fù)雜,對于未來自己想要定義一個(gè)類似的注解具有很大的參考意義!下一篇,我們講解一下retry的真正的執(zhí)行邏輯,并對整體的設(shè)計(jì)做一個(gè)介紹和思考總結(jié)文章來源地址http://www.zghlxwxcb.cn/news/detail-685176.html
到了這里,關(guān)于Spring retry(二)- 源碼解析-啟動裝配篇 @EnableRetry的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!