使用Spring AOP重试方法执行
我的一位博客关注者发送了一封电子邮件,要求我显示“ Spring AOP的RealWorld用法”示例。 他提到,在大多数示例中,都演示了Spring AOP在日志记录方法进入/退出或事务管理或安全性检查中的用法。
他想知道Spring AOP在“针对实际问题的真实项目”中的用法。 因此,我想展示如何在我的一个项目中使用Spring AOP来处理一个实际问题。
我们不会在开发阶段遇到任何问题,只有在负载测试期间或仅在生产环境中才知道。
例如:
- 由于网络延迟问题而导致的远程WebService调用失败
- 由于Lock异常等导致数据库查询失败
在大多数情况下,只需重试相同的操作就足以解决此类故障。
让我们看看如果发生任何异常,如何使用Spring AOP自动重试方法执行。 我们可以使用Spring AOP @Around建议为那些需要重试其方法的对象创建代理,并在Aspect中实现重试逻辑。
在继续实施这些Spring Advice和Aspect之前,首先让我们编写一个简单的实用程序来执行“任务” ,该任务将自动重试N次,而忽略给定的异常集。
public interface Task<T> {T execute();
}
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class TaskExecutionUtil
{private static Logger logger = LoggerFactory.getLogger(TaskExecutionUtil.class);@SafeVarargspublic static <T> T execute(Task<T> task, int noOfRetryAttempts, long sleepInterval, Class<? extends Throwable>... ignoreExceptions) {if (noOfRetryAttempts < 1) {noOfRetryAttempts = 1;}Set<Class<? extends Throwable>> ignoreExceptionsSet = new HashSet<Class<? extends Throwable>>();if (ignoreExceptions != null && ignoreExceptions.length > 0) {for (Class<? extends Throwable> ignoreException : ignoreExceptions) {ignoreExceptionsSet.add(ignoreException);}}logger.debug("noOfRetryAttempts = "+noOfRetryAttempts);logger.debug("ignoreExceptionsSet = "+ignoreExceptionsSet);T result = null;for (int retryCount = 1; retryCount <= noOfRetryAttempts; retryCount++) {logger.debug("Executing the task. Attemp#"+retryCount);try {result = task.execute();break;} catch (RuntimeException t) {Throwable e = t.getCause();logger.error(" Caught Exception class"+e.getClass());for (Class<? extends Throwable> ignoreExceptionClazz : ignoreExceptionsSet) {logger.error(" Comparing with Ignorable Exception : "+ignoreExceptionClazz.getName());if (!ignoreExceptionClazz.isAssignableFrom(e.getClass())) {logger.error("Encountered exception which is not ignorable: "+e.getClass());logger.error("Throwing exception to the caller");throw t;}}logger.error("Failed at Retry attempt :" + retryCount + " of : " + noOfRetryAttempts);if (retryCount >= noOfRetryAttempts) {logger.error("Maximum retrial attempts exceeded.");logger.error("Throwing exception to the caller");throw t;}try {Thread.sleep(sleepInterval);} catch (InterruptedException e1) {//Intentionally left blank}}}return result;}}
我希望这种方法可以自我解释。 它会占用一个Task 并重试noOfRetryAttempts次,以防万一task.execute()方法抛出任何Exception且ignoreExceptions指示重试时要忽略的异常类型。
现在让我们创建一个Retry注释,如下所示:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Retry {public int retryAttempts() default 3;public long sleepInterval() default 1000L; //millisecondsClass<? extends Throwable>[] ignoreExceptions() default { RuntimeException.class };}
我们将使用此@Retry批注来划分需要重试的方法。
现在让我们实现适用于带有@Retry批注的方法的Aspect。
import java.lang.reflect.Method;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Component
@Aspect
public class MethodRetryHandlerAspect {private static Logger logger = LoggerFactory.getLogger(MethodRetryHandlerAspect.class);@Around("@annotation(com.sivalabs.springretrydemo.Retry)")public Object audit(ProceedingJoinPoint pjp) {Object result = null;result = retryableExecute(pjp);return result;}protected Object retryableExecute(final ProceedingJoinPoint pjp){MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();logger.debug("-----Retry Aspect---------");logger.debug("Method: "+signature.toString());Retry retry = method.getDeclaredAnnotation(Retry.class);int retryAttempts = retry.retryAttempts();long sleepInterval = retry.sleepInterval();Class<? extends Throwable>[] ignoreExceptions = retry.ignoreExceptions();Task<Object> task = new Task<Object>() {@Overridepublic Object execute() {try {return pjp.proceed();} catch (Throwable e) {throw new RuntimeException(e);}}};return TaskExecutionUtil.execute(task, retryAttempts, sleepInterval, ignoreExceptions);}
}
而已。 我们只需要一些测试用例即可对其进行实际测试。
首先创建AppConfig.java配置类,如下所示:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AppConfig {}
以及几个虚拟Service Bean。
import org.springframework.stereotype.Service;@Service
public class ServiceA {private int counter = 1;public void method1() {System.err.println("----method1----");}@Retry(retryAttempts=5, ignoreExceptions={NullPointerException.class})public void method2() {System.err.println("----method2 begin----");if(counter != 3){counter++;throw new NullPointerException();}System.err.println("----method2 end----"); }
}
import java.io.IOException;
import org.springframework.stereotype.Service;@Service
public class ServiceB {@Retry(retryAttempts = 2, ignoreExceptions={IOException.class})public void method3() {System.err.println("----method3----");if(1 == 1){throw new ArrayIndexOutOfBoundsException();}}@Retrypublic void method4() {System.err.println("----method4----");}
}
最后编写一个简单的Junit测试来调用这些方法。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=AppConfig.class)
public class RetryTest
{@Autowired ServiceA svcA;@Autowired ServiceB svcB;@Testpublic void testA(){svcA.method1();}@Testpublic void testB(){svcA.method2();}@Test(expected=RuntimeException.class)public void testC(){svcB.method3();}@Testpublic void testD(){svcB.method4();}
}
是的,我知道我可以将这些测试方法编写得更好一些,但是我希望您能理解。
运行JUnit测试并观察log语句,以验证是否在发生Exception的情况下重试方法。
- 情况1:调用ServiceA.method1()时,根本不会应用MethodRetryHandlerAspect。
- 情况2:调用ServiceA.method2()时,我们正在维护一个计数器并抛出NullPointerException 2次。 但是我们已经标记了该方法以忽略NullPointerExceptions。 因此它将继续重试5次。 但是第三次方法将正常执行并正常退出该方法。
- 案例3:调用ServiceB.method3()时,我们将抛出ArrayIndexOutOfBoundsException,但该方法被标记为仅忽略IOException。 因此,将不会重试此方法的执行,并且会立即引发Exception。
- 情况4:调用ServiceB.method4()时,一切都很好,因此通常应在第一次尝试中退出。
我希望这个例子能说明Spring AOP在现实世界中有足够的用处:-)
翻译自: https://www.javacodegeeks.com/2016/02/retrying-method-execution-using-spring-aop.html
使用Spring AOP重试方法执行相关推荐
- spring aop不执行_使用Spring AOP重试方法执行
spring aop不执行 我的一位博客关注者发送了一封电子邮件,要求我显示" Spring AOP的RealWorld用法"示例. 他提到,在大多数示例中,都演示了Spring ...
- Spring 5 中文解析之核心篇-Spring AOP编程
技术交流群: 面向切面的编程(AOP)通过提供另一种思考程序结构的方式来补充面向对像的编程(OOP).OOP中模块化的关键单元是类,而在AOP中模块化是切面.切面使关注点(例如事务管理)的模块化可以跨 ...
- 使用Spring AOP和番石榴速率限制器的节气门方法
外部服务或API可能有使用限制,或者它们不能失败就无法处理大量请求. 这篇文章解释了如何创建一个基于Spring Framework的方面,该方面可以用来限制使用Guava速率限制器的任何建议方法调用 ...
- 使用Spring AOP和Guava速率限制器的节气门方法
外部服务或API可能有使用限制,或者它们无法处理请求负载而不会失败. 这篇文章解释了如何创建一个基于Spring Framework的方面,该方面可以用来限制使用Guava速率限制器的任何建议方法调用 ...
- Spring AOP官网学习
Spring AOP官网学习 5.1 AOP概念 让我们从定义一些核心的AOP概念和术语开始.这些术语并不是spring特有的.不幸的是,AOP术语不是特别直观. 1.Aspect(方面):跨多个类的 ...
- Spring Aop的应用
2019独角兽企业重金招聘Python工程师标准>>> AOP的基本概念 连接点( Jointpoint) : 表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化.方法执行 ...
- Spring AOP(一):概览
一.对AOP的初印象 首先先给出一段比较专业的术语(来自百度): 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方 式和运行期动态代理实 ...
- Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration
Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration Spring Framework是基于两 ...
- Spring AOP知识详解
本文来详细说下spring中的aop内容 文章目录 Spring AOP概述 Spring AOP一代 Pointcut Jointpoint Advice Advisor 织入 Spring AOP ...
最新文章
- tpcc mysql 基准测试_使用tpcc-mysql 对mysql进行基准测试
- 解决Moodle日历乱码的最佳方案
- 3em html5,谁是最好的手机浏览器:IE9 VS Safari 5
- Linux-CentOS 重置root密码
- Matlab在概率统计中的应用问题及解决方案集锦
- 10.1——为什么方法不能用static修饰
- who whoami who am i的区别
- 4.Lucene3.案例介绍,创建索引,查询等操作验证
- 参加Tech.Ed 2006北京行
- python——import导入模
- Kubernetes之集群环境搭建
- 蚂蚁金服分布式链路跟踪组件 SOFATracer 总览 | 剖析
- 如何在云服务器上装系统吗,如何在云服务器上装系统吗
- python如何解析xml请求 http_怎么用python处理xml请求和xml响应,wsdl, soap,希望有源码参考。...
- 2022-2028全球骨科创伤植入物行业调研及趋势分析报告
- 【每日一具18】基于HTTP协议的局域网文件共享软件
- sublime php code sniffer,Sublime插件CodeSniffer配置
- [转帖]DRAM芯片战争,跨越40年的生死搏杀
- visual studio 2019/2022 安装时卡住,一直正在提取文件时的亲测有效的解决方案
- 报错No protocol specified解决办法
热门文章
- Java和Android中的注解
- Oracle入门(十四.1)之PL / SQL简介
- 漫画:什么是ConcurrentHashMap
- Tomcat处理一个HTTP请求的过程
- 全国教学交流研讨会“教学为本”主题总结
- ssh(Spring+Spring mvc+hibernate)——EmpController
- 使用阿里云火车票查询接口案例——CSDN博客
- 最全三大框架整合(使用映射)——applicationContext.xml里面的配置
- 二叉树的前中后序查找+思路分析
- 如何安装mysql5.5.6_centos6安装mysql5.5.53