我创建了一个Spring方面来处理Retry机制。我还创建了一个Retry注释。以下是重试注释的代码以及处理此注释的方面。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Retry { /** * List of exceptions for which we need to retry method invocation. * * @return Array of classes. */ Class<?>[] exceptions(); /** * Number of retries. Default is 3. * * @return Number of retires. */ int retries() default 3; /** * Back of period in ms. Default is 1000 ms. * * @return Back off Period. */ int backOffPeriod() default 1000; } @Aspect public class RetryInterceptor implements Ordered { private static final RetryInterceptor instance = new RetryInterceptor(); private RetryInterceptor() { } private static final Log logger = LogFactory.getLog(RetryInterceptor.class); private int order = 100; @Around("@annotation(retry)") public Object performOperation(ProceedingJoinPoint pjp, Retry retry) throws Throwable { Class<?>[] exceptionClasses = retry.exceptions(); Assert.notEmpty(exceptionClasses, "Exception classes cannot be empty."); int retries = retry.retries(); if (logger.isInfoEnabled()) { logger.info("Attempting to call " + pjp.toShortString() + " with potential for " + getExceptionClasses(exceptionClasses) + " with maximum " + retries + " retries"); } int numAttempts = 0; do { try { return pjp.proceed(); } catch (Throwable ex) { // if the exception is not what we're looking for, pass it through boolean canThrowException = true; for (Class<?> exceptionClass : exceptionClasses) { if (exceptionClass.isAssignableFrom(ex.getClass())) { canThrowException = false; break; } } // A non-configured exception was found. if (canThrowException) { throw ex; } // we caught the configured exception, retry unless we've reached the maximum if (++numAttempts > retries) { logger.warn("Caught " + ex.getClass().getCanonicalName() + " and exceeded maximum retries (" + retries + "), rethrowing."); throw ex; } if (logger.isInfoEnabled()) { logger.info("Caught " + ex.getClass().getCanonicalName() + " and will retry, attempts: " + numAttempts); } } sleep(retry.backOffPeriod()); } while (numAttempts <= retries); // this will never execute - we will have either successfully returned or re-thrown an // exception return null; } @Override public int getOrder() { return order; } private String getExceptionClasses(Class<?>[] classes) { StringBuilder builder = new StringBuilder(); builder.append(classes[0].getCanonicalName()); for (int i = 1; i < classes.length; i++) { builder.append(", ").append(classes[i].getCanonicalName()); } return builder.toString(); } public static RetryInterceptor getInstance() { return instance; } // Better than Thread.sleep(). public void sleep(long backOffPeriod) throws InterruptedException { Object mutex = new Object(); synchronized (mutex) { mutex.wait(backOffPeriod); } } }
要启用注释,我需要实例化RetryInterceptor类。我要确保对于给定的上下文,只有该对象的一个实例。如果由于某种原因创建了多个对象,那么我的建议将被应用多次。我如何才能完全确保始终有1个实例?
我找到了一种方法:)引用:超越DI 我在我的根上下文中注册了BeanDefinitionRegistryPostProcessor,这将确保只有我想要的类的BeanDefinition。
package test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import com.xx.xx.xx.xx.xx.RetryInterceptor; public class TestBeanFacotryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { String[] definitionNames = registry.getBeanDefinitionNames(); for (int i = 0, j = 0; i < definitionNames.length; i++) { Class<?> clazz; try { clazz = Class.forName(registry.getBeanDefinition(definitionNames[i]).getBeanClassName()); if (RetryInterceptor.class == clazz && j++ > 0) { registry.removeBeanDefinition(definitionNames[i]); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }