Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 确保单个spring托管bean实例_Java_Spring_Singleton_Spring Aop_Spring Retry - Fatal编程技术网

Java 确保单个spring托管bean实例

Java 确保单个spring托管bean实例,java,spring,singleton,spring-aop,spring-retry,Java,Spring,Singleton,Spring Aop,Spring Retry,我创建了一个spring方面来处理重试机制。我还创建了一个重试注释。 以下是重试注释的代码和处理此注释的方面 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Retry { /** * List of exceptions for which we need to retry method invocation. * * @return Ar

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

@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);
        }
    }
}
@Retention(RetentionPolicy.RUNTIME)
@目标(ElementType.METHOD)
公共@接口重试{
/**
*需要重试方法调用的异常列表。
* 
*@return类数组。
*/
类[]异常();
/**
*重试次数。默认值为3。
* 
*@返回退休人数。
*/
int retries()默认值为3;
/**
*以毫秒为单位的周期后移。默认值为1000毫秒。
* 
*@返回退避期。
*/
int backOffPeriod()默认值为1000;
}
@面貌
公共类RetryInterceptor实现有序{
私有静态最终RetryInterceptor实例=新RetryInterceptor();
专用重试接收器(){
}
私有静态最终日志记录器=LogFactory.getLog(RetryInterceptor.class);
私人整数阶=100;
@大约(“@注释(重试)”)
公共对象性能操作(ProceedingJoinPoint pjp,重试)抛出可丢弃的{
类[]exceptionClasses=retry.exceptions();
notEmpty(exceptionClasses,“Exception类不能为空”);
int retries=retry.retries();
如果(logger.IsInfo已启用()){
logger.info(“正在尝试调用”+pjp.toShortString()+”,可能是“+GetExceptionClass(ExceptionClass))
+“最大”+重试次数+“重试次数”);
}
int numatempts=0;
做{
试一试{
返回pjp.procedure();
}捕获(可丢弃的ex){
//如果异常不是我们要查找的,请传递它
布尔canThrowException=true;
for(类exceptionClass:exceptionClasses){
if(exceptionClass.isAssignableFrom(例如getClass())){
canThrowException=false;
打破
}
}
//发现未配置的异常。
if(canThrowException){
掷骰子;
}
//我们捕获了配置的异常,请重试,除非已达到最大值
如果(++NUMATEMPTS>重试){
logger.warn(“捕获”+ex.getClass().getCanonicalName()+”并超过最大重试次数(“+retries
+);或;
掷骰子;
}
如果(logger.IsInfo已启用()){
logger.info(“捕获”+ex.getClass().getCanonicalName()+”并将重试,尝试次数:“+numAttempts”);
}
}
休眠(重试.回退周期());

}而(numatempts将只有一个实例作为spring托管bean,默认作用域为singleton。在类的顶部有一些singleton类型的东西(比如静态创建新实例的位置等)…这是不需要的。一个方面就像其他任何方面一样,只是一个bean,所以请将其编码为asuch。如果您想确定,请使用带有一些日志记录的Post-Contract方法,如“Initiating aspect”或其他什么,您将看到它只打印到您的日志中一次

  • 如果有人可以创建您类型的第二个bean,为什么您不想检查有人创建具有相同逻辑的另一个方面?我认为您的方法在设计上是错误的
  • 您可以实现ApplicationContextAware接口,并在那里检查,您的类中只有一个bean存在于上下文中,如果它不是真的,则抛出异常,但如果您有上下文层次结构,我不确定这是否有效
  • 我找到了一种方法:)参考: 我在根上下文中注册了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();
                }
            }
        }
    }
    
    封装测试;
    导入org.springframework.beans.BeansException;
    导入org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    导入org.springframework.beans.factory.support.BeanDefinitionRegistry;
    导入org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    导入com.xx.xx.xx.xx.xx.xx.RetryInterceptor;
    公共类TestBeanFactoryPostProcessor实现BeanDefinitionRegistryPostProcessor{
    @凌驾
    public void后处理beanFactory(ConfigurableListableBeanFactory beanFactory)引发BeanException{
    }
    @凌驾
    公共无效后处理BeanDefinitionRegistry(BeanDefinitionRegistry注册表)引发BeanException{
    String[]definitionNames=注册表.getBeanDefinitionNames();
    for(int i=0,j=0;i0){
    registry.removeBeanDefinition(定义名称[i]);
    }
    }catch(classnotfounde异常){
    e、 printStackTrace();
    }
    }
    }
    }
    
    我知道这一点,但如果在我用id1实例化这个bean的一个XML中,其他人用id2实例化它,那么两个方面将为同一个切入点注册,这是一个问题。另外,如果您注意到我将我的方面创建为JAVA singleton,但这仍然没有帮助,因为有人仍然可以使用相同的singleton instance,并在spring上下文中注册两次。啊,好吧,我现在理解你了。如果你自己不能完全控制上下文(例如,如果这会干扰其他团队/开发人员使用)那么,我不认为你想要什么是可能的,因为你刚才给出的理由:-/你能提出任何更好的设计,使我的方法更健壮吗?通过e