解决线程中的异常无法捕获问题,进行方法重试
方法重试工具类
package com.example.demo.aop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* 方法重试工具类
* 场景描述:
* 在项目启动时,有时候会遇到网络问题,配置同步问题等,导致启动失败,然后需要进行重试操作。
* 而普通的aop,spring-retry,guaua-retry等都是针对主线程中发生异常时,去进行的重试操作。
* 当异常发生在子线程内部时,则捕获不到,导致切面拦截不到异常,进而重试失败。
*-----------------------------------------------
* 此工具类为了解决在线程中发生异常时,进行重试操作。
* @creator wangli66
* @create-time 15:24 2019/11/20
**/
public class RetryUtil1 {
private static Logger logger = LoggerFactory.getLogger(RetryUtil1.class);
private static volatile AtomicInteger retryTimes = new AtomicInteger(-1);
private static final Integer DEFAULT_RETRY_TIMES = 1;
private static final Long DEFAULT_SLEEP_TIME = 3000L;
// 任务队列,先进先出
private static volatile Queue<Task> taskQueue = new LinkedBlockingDeque<>();
private static Set<String> threadNameSet = new HashSet<>();
/*
* @Description: 重试方法
* @param: [objects],需要重试方法的入参
* @return: void
* @Date: 2019/11/20
*/
public static void retry(@NonNull String mName, Integer number, Long inteval, Object... objects) {
logger.info("进入retry的线程为:..." + Thread.currentThread().getName() + ",当前为第:" + retryTimes.get() + "次");
if (retryTimes.get() == 0) {
return;
}
// 初始化
if (retryTimes.get() == -1) {
if (number == null || number <= 0) {
number = DEFAULT_RETRY_TIMES;
}
retryTimes.set(number);
}
if (inteval == null || inteval <= 0) {
inteval = DEFAULT_SLEEP_TIME;
}
retryTimes.decrementAndGet();
try {
Thread.currentThread().sleep(inteval);
} catch (InterruptedException e) {
e.printStackTrace();
}
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
String targetClassName = stackTraces[2].getClassName();
String targetMethod = stackTraces[2].getMethodName();
if (targetClassName.contains("$") && !targetClassName.contains("lambda")) {
targetClassName = targetClassName.substring(0, targetClassName.indexOf("$"));
targetMethod = mName;
}
logger.info("目标类类名:" + targetClassName);
logger.info("目标方法名:" + targetMethod);
try {
Class<?> aClass = Class.forName(targetClassName);
Object targetObj = aClass.newInstance();
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
logger.info("当前方法名:" + method.getName());
method.setAccessible(true);
if (null != method.getName() && targetMethod.equals(method.getName())) {
logger.info("调用方法:" + method.getName());
method.invoke(targetObj, objects);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static synchronized void retryByTask(Task task) {
// 使用方法签名,来判断是否同一方法,然后拿到终止条件
if (taskQueue.size() == 0) {
// if (threadNameSet.add(task.getTargetMethod())) {
if (threadNameSet.add(task.getMethodSign())) {
taskQueue.add(task);
dealwithTask();
}
} else {
// if(threadNameSet.add(task.getTargetMethod())) {
if (threadNameSet.add(task.getMethodSign())) {
taskQueue.add(task);
}
}
}
public static void dealwithTask() {
Iterator<Task> iterator = taskQueue.iterator();
while (iterator.hasNext()) {
Task t = iterator.next();
// 开始处理任务
start(t);
// 移除当前处理的任务
iterator.remove();
}
}
public static void start(Task t) {
new Thread(() -> {
threadNameSet.add(Thread.currentThread().getId() + "");
for (int i = 0; i < t.getTimes(); i++) {
try {
Thread.currentThread().sleep(t.getWaitTime());
Class<?> aClass = Class.forName(t.getTargetClass());
String targetMethod = t.getTargetMethod();
Object targetObj = aClass.newInstance();
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
logger.info("当前方法名:" + method.getName());
method.setAccessible(true);
// 可能存在方法重载,导致方法名一直的情况
if (null != method.getName() && targetMethod.equals(method.getName())) {
logger.info("调用方法:" + method.getName());
Parameter[] parameters = method.getParameters();
if (compareArray(parameters, t.getArgs())) {
method.invoke(targetObj, t.getArgs());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println(e.getCause());
} catch (InterruptedException e) {
e.printStackTrace();
}
// 当最后一次时,把该方法签名移除
// if(i == (t.getTimes() - 1)) {
// threadNameSet.remove(t.getMethodSign());
// }
}
}).start();
}
private static String getObjectType(String typeName) {
if ("int".equals(typeName)) {
return "java.lang.Integer";
}
if ("long".equals(typeName)) {
return "java.lang.Long";
}
if ("double".equals(typeName)) {
return "java.lang.Double";
}
if ("float".equals(typeName)) {
return "java.lang.Float";
}
if ("byte".equals(typeName)) {
return "java.lang.Byte";
}
if ("short".equals(typeName)) {
return "java.lang.Short";
}
if ("char".equals(typeName)) {
return "java.lang.Character";
}
if ("boolean".equals(typeName)) {
return "java.lang.Boolean";
}
return null;
}
/*
* @Description: 比较参数列表是否相等
* @param: [objects1, objects2]
* @return: boolean
* @Date: 2019/11/22
*/
public static boolean compareArray(Parameter[] objects1, Object[] objects2) {
if(objects1.length == 0 && objects2.length == 0) {
return true;
}
if(objects1.length == 0 || objects2.length == 0) {
return false;
}
List<String> list1 = Arrays.stream(objects1)
.filter((obj) -> obj == null ? false : true)
.map((obj1) -> {
if (obj1.getType().isPrimitive()) {
return getObjectType(obj1.getType().getTypeName());
} else {
return obj1.getType().getTypeName().toString();
}
})
.collect(Collectors.toList());
List<String> list2 = Arrays.stream(objects2)
.filter((obj) -> obj == null ? false : true)
.map((obj1) -> obj1.getClass().getTypeName())
.collect(Collectors.toList());
System.out.println("list1:" +list1+"\nlist2:"+list2);
// 两个list引用相同(包括两者都为空指针的情况)
if (list1 == list2) {
return true;
}
// 两个list元素个数不相同
if (list1.size() != list2.size()) {
return false;
}
// 两个list元素个数已经相同,再比较两者内容
// 采用这种可以忽略list中的元素的顺序
// 涉及到对象的比较是否相同时,确保实现了equals()方法
if (!list1.containsAll(list2)) {
return false;
}
return true;
}
}
任务类
package com.example.demo.aop;
/**
* @creator wangli66
* @create-time 10:32 2019/11/21
**/
public class Task {
private String targetClass;
private String targetMethod;
private Object[] args;
private Integer times;
private Long waitTime;
private String methodSign;
public String getMethodSign() {
return methodSign;
}
public void setMethodSign(String methodSign) {
this.methodSign = methodSign;
}
public Task() {
}
public Task(String targetClass, String targetMethod, Object[] args, Integer times, Long waitTime, String methodSign) {
this.targetClass = targetClass;
this.targetMethod = targetMethod;
this.args = args;
this.times = times;
this.waitTime = waitTime;
this.methodSign = methodSign;
}
public String getTargetClass() {
return targetClass;
}
public void setTargetClass(String targetClass) {
this.targetClass = targetClass;
}
public String getTargetMethod() {
return targetMethod;
}
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Integer getTimes() {
return times;
}
public void setTimes(Integer times) {
this.times = times;
}
public Long getWaitTime() {
return waitTime;
}
public void setWaitTime(Long waitTime) {
this.waitTime = waitTime;
}
}
自定义异常类
package com.example.demo.retry;
/**
* @creator wangli66
* @create-time 14:10 2019/11/21
**/
public class RetryException extends RuntimeException {
// 提供无参数的构造方法
public RetryException() {
}
// 提供一个有参数的构造方法,可自动生成
public RetryException(String message) {
super(message);// 把参数传递给Throwable的带String参数的构造方法
}
}
工作类
package com.example.demo.retry;
import com.example.demo.aop.RetryAnno;
import com.example.demo.aop.RetryUtil1;
import com.example.demo.aop.Task;
import com.example.demo.myenum.UserTest;
import org.springframework.stereotype.Service;
import java.util.Arrays;
/**
* @creator wangli66
* @create-time 14:10 2019/11/21
**/
@Service
public class Bizwork {
public int work(int i) {
System.out.println("work.........有参:"+i);
if(i == 1) {
throw new RetryException("自定义异常");
}
return 0;
}
public void work1() {
System.out.println("work........无参");
new Thread(()-> {
System.out.println("线程异常:"+Thread.currentThread().getId());
try {
throw new RetryException();
} catch (RetryException e) {
// 设置参数
Task task = new Task();
task.setTargetClass(Bizwork.class.getName());
task.setTargetMethod("work1");
task.setArgs(new Object[0]);
task.setTimes(3);
task.setWaitTime(3000L);
task.setMethodSign(getMethodSign(null));
System.out.println("方法签名:"+getMethodSign(null));
// 调用重试工具类
RetryUtil1.retryByTask(task);
}
}).start();
}
public String plus(int i1, int i2, UserTest userTest) {
System.out.println("plus..........");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("plus...run..........");
try {
throw new RetryException();
} catch (RetryException e) {
// 生成该方法签名
String methodSign = getMethodSign(i1,i2,userTest);
System.out.println("方法签名:"+methodSign);
// 调用重试工具类
RetryUtil1.retryByTask(new Task(
Bizwork.class.getName(),
"plus",
new Object[]{i1,i2,userTest},
1,
1000l,
methodSign));
}
}
}).start();
return null;
}
@RetryAnno(waitDesc = 3000L,maxAttempt = 3,retryThrowable = Exception.class)
public void testAnno() {
throw new RetryException("测试注解");
}
public String getMethodSign(Object ...objects) {
StringBuffer sb = new StringBuffer();
// 拿到方法的全类名
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
// 拿到方法的参数列表,空的话为null
sb.append(methodName).append("^");
if(objects == null) {
return sb.append("null").toString();
}
Arrays.stream(objects).forEach((obj) -> {
if(obj == null ) {
sb.append("null");
}else {
sb.append(obj.getClass().toString());
}
});
return sb.toString();
}
}
注解
package com.example.demo.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @creator wangli66
* @create-time 20:41 2019/11/20
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryAnno {
int maxAttempt() default 0;
long waitDesc() default 0;
Class retryThrowable() default Exception.class;
}
AOP切面
package com.example.demo.retry;
import com.example.demo.aop.RetryAnno;
import com.example.demo.aop.RetryUtil1;
import com.example.demo.aop.Task;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.UUID;
/**
* @describtion aop异常通知
* @creator wangli66
* @create-time 14:19 2019/11/21
**/
@Aspect
@Component
public class BizAspect {
/*
* @Description: 在注解中直接定义切点的方式
* @param: [e]
* @return: void
* @Date: 2019/11/26
*/
@AfterThrowing(throwing = "e",pointcut = "execution(* com.example.demo.retry.Bizwork.*(..))")
public void testThrowing(Exception e) {
System.out.println("aspect.........");
}
/*
* @Description: 单独定义切点
* @param: []
* @return: void
* @Date: 2019/11/26
*/
@Pointcut("execution(* com.example.demo.retry.Bizwork.*(..))")
public void workCut() {
}
/*
* @Description: 引入切点
* @param: [joinPoint]
* @return: void
* @Date: 2019/11/26
*/
@AfterThrowing("workCut()")
public void testPointCut(JoinPoint joinPoint) {
System.out.println("testPointCut.......");
Task task = new Task();
// 设置参数
task.setTargetClass(joinPoint.getSignature().getDeclaringTypeName());
task.setTargetMethod(joinPoint.getSignature().getName());
task.setArgs(joinPoint.getArgs());
task.setTimes(3);
task.setWaitTime(3000L);
// 调用重试工具类
RetryUtil1.retryByTask(task);
}
/*
* @Description: 使用自定义注解方式
* @param: []
* @return: void
* @Date: 2019/11/26
*/
@Pointcut(value = "@annotation(com.example.demo.aop.RetryAnno)")
public void testAnnoCut() {
}
/*
* @Description: 注解方式
* @param: [joinPoint]
* @return: void
* @Date: 2019/11/21
*/
@AfterThrowing(value = "@annotation(com.example.demo.aop.RetryAnno) && @annotation(retryAnno)")
public void testAnnotion(JoinPoint joinPoint,RetryAnno retryAnno) {
System.out.println("----注解方式------------------------"+retryAnno.maxAttempt());
Task task = new Task();
// 设置参数
task.setTargetClass(joinPoint.getSignature().getDeclaringTypeName());
task.setTargetMethod(joinPoint.getSignature().getName());
task.setArgs(joinPoint.getArgs());
task.setTimes(retryAnno.maxAttempt());
task.setWaitTime(retryAnno.waitDesc());
// 调用重试工具类
RetryUtil1.retryByTask(task);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类