我的一位博客关注者发送了一封电子邮件,要求我显示“ 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重试方法执行相关推荐

  1. spring aop不执行_使用Spring AOP重试方法执行

    spring aop不执行 我的一位博客关注者发送了一封电子邮件,要求我显示" Spring AOP的RealWorld用法"示例. 他提到,在大多数示例中,都演示了Spring ...

  2. Spring 5 中文解析之核心篇-Spring AOP编程

    技术交流群: 面向切面的编程(AOP)通过提供另一种思考程序结构的方式来补充面向对像的编程(OOP).OOP中模块化的关键单元是类,而在AOP中模块化是切面.切面使关注点(例如事务管理)的模块化可以跨 ...

  3. 使用Spring AOP和番石榴速率限制器的节气门方法

    外部服务或API可能有使用限制,或者它们不能失败就无法处理大量请求. 这篇文章解释了如何创建一个基于Spring Framework的方面,该方面可以用来限制使用Guava速率限制器的任何建议方法调用 ...

  4. 使用Spring AOP和Guava速率限制器的节气门方法

    外部服务或API可能有使用限制,或者它们无法处理请求负载而不会失败. 这篇文章解释了如何创建一个基于Spring Framework的方面,该方面可以用来限制使用Guava速率限制器的任何建议方法调用 ...

  5. Spring AOP官网学习

    Spring AOP官网学习 5.1 AOP概念 让我们从定义一些核心的AOP概念和术语开始.这些术语并不是spring特有的.不幸的是,AOP术语不是特别直观. 1.Aspect(方面):跨多个类的 ...

  6. Spring Aop的应用

    2019独角兽企业重金招聘Python工程师标准>>> AOP的基本概念 连接点( Jointpoint) : 表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化.方法执行 ...

  7. Spring AOP(一):概览

    一.对AOP的初印象 首先先给出一段比较专业的术语(来自百度): 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方 式和运行期动态代理实 ...

  8. Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration

    Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration Spring Framework是基于两 ...

  9. Spring AOP知识详解

    本文来详细说下spring中的aop内容 文章目录 Spring AOP概述 Spring AOP一代 Pointcut Jointpoint Advice Advisor 织入 Spring AOP ...

最新文章

  1. tpcc mysql 基准测试_使用tpcc-mysql 对mysql进行基准测试
  2. 解决Moodle日历乱码的最佳方案
  3. 3em html5,谁是最好的手机浏览器:IE9 VS Safari 5
  4. Linux-CentOS 重置root密码
  5. Matlab在概率统计中的应用问题及解决方案集锦
  6. 10.1——为什么方法不能用static修饰
  7. who whoami who am i的区别
  8. 4.Lucene3.案例介绍,创建索引,查询等操作验证
  9. 参加Tech.Ed 2006北京行
  10. python——import导入模
  11. Kubernetes之集群环境搭建
  12. 蚂蚁金服分布式链路跟踪组件 SOFATracer 总览 | 剖析
  13. 如何在云服务器上装系统吗,如何在云服务器上装系统吗
  14. python如何解析xml请求 http_怎么用python处理xml请求和xml响应,wsdl, soap,希望有源码参考。...
  15. 2022-2028全球骨科创伤植入物行业调研及趋势分析报告
  16. 【每日一具18】基于HTTP协议的局域网文件共享软件
  17. sublime php code sniffer,Sublime插件CodeSniffer配置
  18. [转帖]DRAM芯片战争,跨越40年的生死搏杀
  19. visual studio 2019/2022 安装时卡住,一直正在提取文件时的亲测有效的解决方案
  20. 报错No protocol specified解决办法

热门文章

  1. Java和Android中的注解
  2. Oracle入门(十四.1)之PL / SQL简介
  3. 漫画:什么是ConcurrentHashMap
  4. Tomcat处理一个HTTP请求的过程
  5. 全国教学交流研讨会“教学为本”主题总结
  6. ssh(Spring+Spring mvc+hibernate)——EmpController
  7. 使用阿里云火车票查询接口案例——CSDN博客
  8. 最全三大框架整合(使用映射)——applicationContext.xml里面的配置
  9. 二叉树的前中后序查找+思路分析
  10. 如何安装mysql5.5.6_centos6安装mysql5.5.53