问题
项目中往往有很多定时任务,在本地执行时,定时任务的日志混杂在一起,不好分辨,如何方便快速的管理定时任务呢?
解决
使用Aop在定时任务执行前进行切入,根据配置文件判断这个定时任务是否要执行,决定继续执行或直接退出此任务。
步骤
1、引入Spring的Aop依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<scope>provided</scope>
</dependency>
2、定时任务权限配置
aaa:
routes:
- className: '这是某个定时任务的类名'
run: 'true'
3、定义切面
@Aspect
@Slf4j
@Configuration
//读取配置文件
@ConfigurationProperties("aaa")
public class ScheduleAop {
@Setter
private List<Map<String, String>> routes;
//切入点为第四步中配置的注解
@Around("@annotation(xxx.EnableScheduleMethod)")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.debug("=====SysLogAspect 环绕通知开始=====");
log.debug("获取定时任务类名{}", joinPoint.getTarget().getClass().getName());
log.debug("获取定时任务方法名{}", joinPoint.getSignature().getName());
log.debug("定时任务的target{}", joinPoint.getTarget());
String thisClassname = joinPoint.getTarget().getClass().getName();
//获取这个定时任务名对应的权限
for (Map thisRoute : routes) {
if (thisRoute.get("className").equals(thisClassname) && "true".equals(thisRoute.get("run"))) {
log.debug("执行");
joinPoint.proceed();
} else {
log.debug("未执行");
}
}
log.debug("=====SysLogAspect 环绕通知结束=====");
return joinPoint;
}
}
4、定义注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableScheduleMethod {
}
优化
一、省去自定义注解
去掉自定义注解,直接切到定时任务的@Scheduled注解上。
@Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
二 、解决多人同时本地启动的问题
权限配置时不采用true、false。采用serverName。
利用每个人的maven中settings.xml中的serverName不同,取得当前本地运行的serverName,并与配置文件中的相比较,如果一致,则运行该定时任务。
最终代码
配置文件
schedule:
routes:
包名.类名.方法名: 需要运行的serverName
Aop代码
@Aspect
@Slf4j
@Configuration
//只在dev本地环境生效
@Profile("dev")
public class ScheduleAop {
//注入serverName
@Setter
@Value("${xxx.xx}")
private String serviceName;
@Setter
private Map<String, String> routes;
//切入@Scheduled注解
@Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
public Object doAround(ProceedingJoinPoint joinPoint) {
String thisClassname = joinPoint.getTarget().getClass().getName();
String funcName = joinPoint.getSignature().getName();
String totalName = thisClassname + '.' + funcName;
String serverNames = routes.get(totalName);
if (serverNames != null && serverNames.length() > 0) {
Arrays.stream(serviceName.split(",")).forEach(
thisName -> {
if (serviceName.equals(thisName)) {
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
);
}
return null;
}
}