我有一个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
?
答案 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确定是否可以被覆盖等等),但这取决于你需要的安全程度。