1 概述
项目中一般都会有定时任务的相关案例,那么我们一般会通过java提供的ScheduledExecutorService 或Spring提供的TaskScheduler(也可使用@Scheduled注解)来实现,因为其不需要依赖外部模块,简单实用,但是Spring提供的@Scheduled注解的定时任务并不支持任务的动态开启与关闭,以及排期的动态修改,本案例通过重写Spring的定时任务注解@Scheduled 来使其可以支持应用服务不停止的情况下在线动态修改任务的排期以及启停功能。
案例代码地址:https://github.com/AIFEINIK/Spring-Scheduled
2 重写Scheduled
2.1 自定义JobScheduled注解
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JobScheduled {
/**
* cron 表达式
* @return
*/
String cron() default "";
/**
* 时区
* @return
*/
String zone() default "";
/**
* 是否开启任务,默认开启
* @return
*/
String enable() default "true";
/**
* 下次任务执行时间与上次执行任务之间固定间隔毫秒数
* @return
*/
long fixedDelay() default -1;
String fixedDelayString() default "";
/**
* 以固定速率执行任务
* @return
*/
long fixedRate() default -1;
String fixedRateString() default "";
/**
* 初始延迟时间,毫秒
* @return
*/
long initialDelay() default -1;
String initialDelayString() default "";
}
2.2 自定义注解JobScheduled的解析器类
@Component
public class JobScheduledAnnotationBeanPostProcessor
implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, BeanNameAware,
BeanFactoryAware, ApplicationContextAware, SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private DataConfig config;
@Nullable
private Object scheduler;
@Nullable
private StringValueResolver embeddedValueResolver;
@Nullable
private BeanFactory beanFactory;
@Nullable
private String beanName;
private boolean init = true;
@Nullable
private ApplicationContext applicationContext;
public Map<String, Object> beanMap = new HashMap<>();
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (this.beanFactory == null) {
this.beanFactory = applicationContext;
}
}
@Nullable
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return getObject(bean);
}
public Object getObject(Object bean) {
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
final Set<Method> annotatedMethods = new LinkedHashSet<>(1);
ReflectionUtils.doWithMethods(targetClass, method -> {
Set<JobScheduled> schedules = AnnotationUtils.getRepeatableAnnotations(method, JobScheduled.class, Schedules.class);
for (JobScheduled jobScheduled : schedules) {
processScheduled(jobScheduled, method, bean);
annotatedMethods.add(method);