1

我创建了一个弹簧方面来处理重试机制。我还创建了一个重试注释。以下是重试注释的代码和处理此注释的方面。

@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 个实例?

4

3 回答 3

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();
            }
        }
    }
}
于 2014-07-30T06:53:39.973 回答
0

永远只有一个实例,因为它是一个默认范围为单例的 Spring 托管 bean。您在类的顶部有一些单例类型的东西(例如静态创建新实例的位置等)......这不是必需的。一个方面就像任何其他方面一样只是一个 bean,因此对其进行编码。如果您想确定,请在 PostContruct 方法中添加一些诸如“启动方面”之类的日志记录,您会看到它只打印到您的日志中一次。

于 2014-07-28T13:04:59.953 回答
0
  1. 如果有人可以创建您类型的第二个 bean,为什么您不想检查某人是否创建了具有相同逻辑的另一个方面?我认为您的方法在设计上是错误的。
  2. 您可以实现 ApplicationContextAware 接口并在那里检查,您的类中只有一个 bean 存在于上下文中,如果不正确则抛出异常,但如果您有上下文层次结构,我不确定这是否有效。
于 2014-07-28T13:26:29.693 回答