用于重试注释的Bean post处理器

时间:2018-06-11 11:55:20

标签: java spring proxy

我有一个Spring版本3.0的遗留项目(我不能使用spring包中的Retriable注释)。 我想实现Retryable注释来注释我的方法,哪些执行应该在失败时重试。

这是我的班级:

@Component
public final class RetryBpp implements BeanPostProcessor {
    private final ConcurrentMap<String, ClassDefinition> map = new ConcurrentHashMap<>();

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
        final Class<?> asClass = bean.getClass();
        final Method[] methods = asClass.getMethods();
        final List<Method> collect = Stream.of(methods)
                                           .filter(method -> method.isAnnotationPresent(Repitable.class))
                                           .collect(Collectors.toList());
        if(!collect.isEmpty()){
            this.map.put(beanName,new ClassDefinition(collect,asClass));
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
        final ClassDefinition definition = this.map.get(beanName);
        if(definition != null){
            final Class beanClass = definition.asClass;
            return Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(), (proxy, method, args) -> {
                if(definition.isMethodPresent(method)){
                    System.out.println("Present");
                    return this.retry(definition.originalMethod(method),bean,args);
                } else{
                    return method.invoke(bean,args);
                }
            });
        } else{
            return bean;
        }
    }

    private Object retry(final Method method,final Object originalBean,Object[] argc){
        final Repitable repitable = method.getAnnotation(Repitable.class);
        int attempts = repitable.attempts();
        Throwable exc = null;
        while(attempts!=0){
            try{
                return method.invoke(originalBean,argc);
            }catch (final Throwable throwable){
                exc = throwable;
                attempts--;
                this.sleep(repitable.delay(),repitable.timeUnit());
            }
        }
        throw new RuntimeException(exc);
    }

    @SneakyThrows(InterruptedException.class)
    private void sleep(final int time, final TimeUnit timeUnit){
        timeUnit.sleep(time);
    }

    @AllArgsConstructor
    private static final class ClassDefinition{
        private final List<Method> methods;
        private final Class asClass;
        boolean isMethodPresent(final Method method){
            return this.methods.stream().anyMatch(mthd->mthd.getName().equals(method.getName()));
        }
        Method originalMethod(final Method method){
            return this.methods.stream().filter(mthd->mthd.getName().equals(method.getName())).findFirst().orElseThrow(NullPointerException::new);

        }

    }


}

它有效,但我想改变两件事

1)在重试方法中,我想保留最后一个异常并在repeateCount = 0时抛出,我需要将null ptr声明为exc,但我希望我的所有字段都是最终的。有没有可能的方法来重写我的代码?

2)在ClassDefinition中我将Method按名称进行比较,因为equals的原始Method class方法按类比较,我不能这样做,因为原始类被代理替换,是否可能以不同的方式比较两个Method's

1 个答案:

答案 0 :(得分:0)

对于第一部分:一个选项是使用List的{​​{1}}。然后抛出类似的东西:

Throwables

这也提供了所有失败的好处。

对于后一部分,这可能是复杂而昂贵的,这取决于某些方法是否会出现泛型,变量等等。

由于您已经在使用spring,因此可以尝试组合使用:final RuntimeException theError = new RuntimeException(list.get(list.size() - 1)); for (int i = 0; i < list.size() - 1; i++) { theError.addSuppressed(list.get(i)); } throw theError; AopUtils.getTargetClass来获得您想要的内容(需要重构您传递给某些测试方法的内容)。

通过以下方式提供更多(注意,而非绝对可靠)测试的简单方法:

AopUtils.getMostSpecificMethod

我确定您可以执行更多检查(return methods.stream().anyMatch(mthd -> { if (!method.getName().equals(mthd.getName())) { return false; } if (!method.getReturnType().isAssignableFrom(mthd.getReturnType())) { return false; } final Class<?>[] parameterTypes = method.getParameterTypes(); if (mthd.getParameterTypes().length != parameterTypes.length) { return false; } for (int i = 0; i < parameterTypes.length; i++) { if (!parameterTypes[i].equals(mthd.getParameterTypes()[i])) { return false; } } return true; }); 以确认他们可以覆盖; Method.getExceptionTypes()并从那里开始处理树; {{1确定是否可以被覆盖等等),但这取决于你需要的安全程度。