转载文章:https://cloud.tencent.com/developer/article/2171832
1. 引入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
2. 添加自定义注解接口
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ScheduledLock {
}
3.AOP逻辑
import com.miracle.qaodo.annotation.ScheduledLock;
import lombok.extern.slf4j.Slf4j;
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.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@Slf4j
public class ScheduleLockAspect implements ApplicationContextAware {
//直接autowired redistemplate会报错Unexpected error occurred in scheduled task
//因为@Scheduled注解方式级别高于资源注入级别,导致了资源注入失败
//使用ApplicationContextAware,它实现了这个接口的bean,当spring容器初始化的时候,会自动的将ApplicationContext注入进来
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
public static Object getBean(String name) {
return getContext().getBean(name);
}
@Around(value = "@annotation(scheduledLock)")
public Object around(ProceedingJoinPoint point, ScheduledLock scheduledLock) {
//拦截的类名
Class clazz = point.getTarget().getClass();
//拦截的方法
Method method = ((MethodSignature) point.getSignature()).getMethod();
log.info("定时任务锁 拦截了类:" + clazz + " 方法:" + method);
Object proceed = null;
RedisTemplate<String,String> redisTemplate = (RedisTemplate)getBean("redisTemplate");
if (redisTemplate.opsForValue().setIfAbsent("qdchess-SchdulesLock-" + method.getName(), "lock",10, TimeUnit.SECONDS)) {
//此处的key是可以根据自己的使用情况进行设置,只要方法之间不重复即可。
log.info("其他服务未执行,通过执行");
//获取锁,如果为false说明有其他服务正在执行,跳过执行
try {
proceed = point.proceed(); //执行定时任务
redisTemplate.delete("qdchess-SchdulesLock-" + method.getName());
return proceed;
} catch (Throwable throwable) {
redisTemplate.delete("qdchess-SchdulesLock-" + method.getName());
throwable.printStackTrace();
return null;
}
}
log.info("其他服务已执行,未通过执行");
return proceed;
}
}
4. 使用
@Scheduled(cron = "0,20,40 * * * * ?")
@ScheduledLock
public void testLock(){
log.info(ServerTimer.getFull());
}