spring aop实现接口超时处理组件

文章目录

实现思路

  1. 这里使用FutureTask,它通过get方法以阻塞的方式获取执行结果,并设定超时时间:
java 复制代码
public V get() throws InterruptedException, ExecutionException ;

public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException ;
  1. 利用spring aop解耦业务
  2. 定义业务异常信息

实现代码

定义注解:

java 复制代码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
public @interface TimeoutCheck {

    /**
     * 超时时间,默认5秒
     */
    long timeout() default 5L;

    /**
     * 超时单位,默认秒
     */
    TimeUnit unit() default TimeUnit.SECONDS;

    /**
     * 超时后是否销毁线程
     */
    boolean destroy() default true;
}

这里有一个destroy()的方法,因为我们在执行时开独立线程处理,所以这个方法是为了在超时后,用来判断是否销毁还在执行的线程;

定义异常:

注意:这里的父类应该是项目中的基础业务异常类;

java 复制代码
public class TimeoutCheckException extends RuntimeException{

    public TimeoutCheckException(String message) {
        super(message);
    }

    public TimeoutCheckException(String message, Throwable throwable) {
        super(message, throwable);
    }
}

再顺便定义一个属性配置:

这个的作用是全局控制开关,当不需要的时候可以直接通过配置关闭;

java 复制代码
@Component
@ConfigurationProperties(prefix = "aliweb.timeout")
public class TimeoutCheckProperties {

    private boolean enable = true;

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }
}

最后就是我们的aop类:

java 复制代码
@Aspect
@Component
public class TimeoutAop {

    private static final Logger logger = LoggerFactory.getLogger(TimeoutAop.class);

    @Autowired
    private TimeoutCheckProperties timeoutCheckProperties;

    @Pointcut("@annotation(timeoutCheck)")
    public void pointCut(TimeoutCheck timeoutCheck) {
    }

    @Around(value = "pointCut(timeoutCheck)", argNames = "joinPoint, timeoutCheck")
    public Object around(ProceedingJoinPoint joinPoint, TimeoutCheck timeoutCheck) throws Throwable {
        if (!timeoutCheckProperties.isEnable()) {
            return joinPoint.proceed();
        }
        long timeout = timeoutCheck.timeout();
        if (timeout <= 0) {
            throw new TimeoutCheckException("业务逻辑执行时间不能小于等于0");
        }
        long start = System.currentTimeMillis();
        String msg = null;
        Exception error = null;
        Object data = null;
        FutureTask<Object> futureTask = createTask(joinPoint);
        try {
            Thread thread = new Thread(futureTask);
            thread.start();
            data = futureTask.get(timeout, timeoutCheck.unit());
        } catch (InterruptedException e) {
            msg = "执行中断";
            error = e;
        } catch (ExecutionException e) {
            msg = "执行异常";
            error = e;
        } catch (TimeoutException e) {
            msg = "执行超时";
            error = e;
        } finally {
            futureTask.cancel(timeoutCheck.destroy());
        }
        logger.debug("执行时间:{}", System.currentTimeMillis() - start);
        if (error != null) {
            String suf = error.getMessage() == null ? "" : ":" + error.getMessage();
            logger.error(msg + suf, error);
            throw new TimeoutCheckException(msg + suf, error);
        }
        return data;
    }

    private static FutureTask<Object> createTask(ProceedingJoinPoint joinPoint) {
        return new FutureTask<>(() -> {
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        });
    }

}

starter组件

将功能提取成starter组件:

  1. 定义配置类
java 复制代码
@Configuration
@ComponentScan("com.liry.aliweb.timeout")
public class TimeoutCheckAutoConfig {
}
  1. 定义配置扫描文件spring.factories,路径:

    src/main/resources/META-INF/spring.factories

    properties 复制代码
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.liry.aliweb.timeout.config.TimeoutCheckAutoConfig
  2. pom增加依赖:

    xml 复制代码
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>

如上,在主项目引入时就可以直接使用了

相关推荐
程序员清风9 小时前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
皮皮林55110 小时前
利用闲置 Mac 从零部署 OpenClaw 教程 !
java
NE_STOP13 小时前
springMVC-HTTP消息转换器与文件上传、下载、异常处理
spring
华仔啊16 小时前
挖到了 1 个 Java 小特性:var,用完就回不去了
java·后端
SimonKing16 小时前
SpringBoot整合秘笈:让Mybatis用上Calcite,实现统一SQL查询
java·后端·程序员
日月云棠1 天前
各版本JDK对比:JDK 25 特性详解
java
用户8307196840821 天前
Spring Boot 项目中日期处理的最佳实践
java·spring boot
JavaGuide1 天前
Claude Opus 4.6 真的用不起了!我换成了国产 M2.5,实测真香!!
java·spring·ai·claude code
IT探险家1 天前
Java 基本数据类型:8 种原始类型 + 数组 + 6 个新手必踩的坑
java
花花无缺1 天前
搞懂new 关键字(构造函数)和 .builder() 模式(建造者模式)创建对象
java