确保spring托管bean的单个实例

我创建了一个spring方面来处理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个实例?

永远只有一个实例作为Spring管理bean,默认范围为singleton。 你的class级顶部有一些单例类型的东西(比如静态创建新实例的地方等)……这不是必需的。 一个方面只是一个像任何其他的bean,所以编码它。 如果你想确定,请在PostContruct方法中添加一些日志记录,例如“启动方面”或其他内容,您将看到它只打印到您的日志一次。

  1. 如果有人可以创建你的类型的第二个bean,为什么你不想检查有人用相同的逻辑创建另一个方面? 我认为你的方法在设计上是错误的。
  2. 你可以实现ApplicationContextAware接口并检查那里,你的类中只有一个bean存在于上下文中,如果不是这样,则抛出exception,但是如果你有上下文层次结构,我不确定这是否会起作用。

我找到了一种方法来做到这一点:) Ref: Going Beyond 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(); } } } }