- 一:注解方式配置定时任务:
- 二:@Scheduled
- 三:原理简介
- 四:其他
以前用过这个注解实现定时任务,但是只是使用,现在做项目又用到了这个功能,系统的学习一下~
spring定时任务设置有两种方式,注解和xml配置。推荐使用注解,在本文章也主要介绍注解方式配置
下面的步骤默认spring的其他配置项都已经配置好(比如启动注解配置,包路径扫描等)
1:在spring配置文件中配置,添加命名空间
xmlns:task="http://www.springframework.org/schema/task"
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd"
<task:annotation-driven scheduler="dataScheduler"/>
<task:scheduler id="dataScheduler" pool-size="5"/>
2:使用注解配置定时任务
- 注意 下面的案例是在每天的早上2点执行
- “0 0 2 \* \* \*”是怎么组合的?下面会详细介绍@Scheduled()注解
@Scheduled(cron = "0 0 2 * * *")
public void init(){
todo...
}
在此需要注意:@Scheduled只能注释在无参的方法上,我看网上有许多博客说必须无参无返回值的,但是经过我的测试有返回值是可以的,可能是版本更新了吧。
现在就算是完成spring定时器的使用了,下面让我们来详细的看一下@Scheduled注解吧~
@Scheduled注解是Spring专门为定时任务设计的注解
首先,让我们来看看这个注解是怎么组成的吧(适用于版本JDK8与spring4.3及其以上)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String cron() default "";
String zone() default "";
long fixedDelay() default -1L;
String fixedDelayString() default "";
long fixedRate() default -1L;
String fixedRateString() default "";
long initialDelay() default -1L;
String initialDelayString() default "";
}
从上述代码中看以看出:
1:@Scheduled被注解部分:
上述的所涉及到的注解有不清楚作用的,可以自行baidu\google,网上有好多介绍的文章。
2:@Scheduled参数部分,总共包含8各部分,我们来分别看一下其作用:
cron可以组合出更多的定时情况,fixedDelay和fixedRate只能定义每隔多长时间执行一次。 在上述cron、fixedDelay、fixedRate 只能同时存在一个,使用其中一个就不能使用另外的一个,否则会报错“java.lang.IllegalStateException”
3:cron参数
一个cron表达式可以有6个元素或者7个元素组成(“年”这个元素可以省略,省略之后就是默认“每一年”)
3.1:按顺序依次为:
3.2:每个元素可以接受的值:
字段 | 允许值 | 允许的特殊字符 |
---|---|---|
秒 | 0-59 | , - * / |
分 | 0-59 | , - * / |
小时 | 0-23 | , - * / |
日期 | 1-31 | , - * ? / L W C |
月份 | 1-12 或者 JAN-DEC | , - * / |
星期 | 1-7 或者 SUN-SAT | , - * ? / L C # |
年 | 空, 1970-2099 | , - * / |
3.3:一些特殊字符解释与注意事项,可以结合下面的小案例来理解:
3.4:一些小案例:
到这个地方你应该对@Scheduled有一个较全面的理解了,下面我们就来简单的看一下其实现原理吧~
1:主要过程:
2:上述就是一个大致过程,下面看一下相应的源码:
注意 :spring对定时任务的操作的源码全部在spring-context.jar包下的org.springframework.scheduling包下面,主要包含三部分:annotation、config、 support,大家有兴趣的话可以去看看
1:获取项目中所有被注解 @Scheduled注解的方法
public Object postProcessAfterInitialization(Object bean, String beanName) {
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MetadataLookup<Set<Scheduled>>() {
public Set<Scheduled> inspect(Method method) {
//获取注解方法
**Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);**
return !scheduledMethods.isEmpty() ? scheduledMethods : null;
}
});
if (annotatedMethods.isEmpty()) {
...
} else {
Iterator var5 = annotatedMethods.entrySet().iterator();
while(var5.hasNext()) {
Entry<Method, Set<Scheduled>> entry = (Entry)var5.next();
Method method = (Method)entry.getKey();
Iterator var8 = ((Set)entry.getValue()).iterator();
while(var8.hasNext()) {
Scheduled scheduled = (Scheduled)var8.next();
//将获取的任务进行参数解析并存放到任务队列
this.processScheduled(scheduled, method, bean);
}
}
...
}
}
return bean;
}
2:通过processScheduled方法将所有定时的方法存放在定时任务队列中
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
try {
...
//解析initialDelayString参数
String initialDelayString = scheduled.initialDelayString();
if (StringUtils.hasText(initialDelayString)) {
...
}
//解析cron参数
String cron = scheduled.cron();
if (StringUtils.hasText(cron)) {
...
//存放到任务队列中
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
}
...
//解析fixedDelay参数
long fixedDelay = scheduled.fixedDelay();
if (fixedDelay >= 0L) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
}
String fixedDelayString = scheduled.fixedDelayString();
if (StringUtils.hasText(fixedDelayString)) {
...
//存放到任务队列中
tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
}
//解析fixedRate参数
long fixedRate = scheduled.fixedRate();
if (fixedRate >= 0L) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
}
String fixedRateString = scheduled.fixedRateString();
if (StringUtils.hasText(fixedRateString)) {
...
//存放到任务队列中
tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
}
Assert.isTrue(processedSchedule, errorMessage);
Map var19 = this.scheduledTasks;
//并发控制并将任务存放在map中
synchronized(this.scheduledTasks) {
Set<ScheduledTask> registeredTasks = (Set)this.scheduledTasks.get(bean);
if (registeredTasks == null) {
registeredTasks = new LinkedHashSet(4);
//将任务存放在map中
this.scheduledTasks.put(bean, registeredTasks);
}
((Set)registeredTasks).addAll(tasks);
}
} catch (IllegalArgumentException var26) {
throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + var26.getMessage());
}
}
3:之后交给ScheduledTaskRegistrar类的方法scheduleTasks去添加定时任务
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
Iterator var1;
if (this.triggerTasks != null) {
var1 = this.triggerTasks.iterator();
while(var1.hasNext()) {
TriggerTask task = (TriggerTask)var1.next();
this.addScheduledTask(this.scheduleTriggerTask(task));
}
}
if (this.cronTasks != null) {
var1 = this.cronTasks.iterator();
while(var1.hasNext()) {
CronTask task = (CronTask)var1.next();
this.addScheduledTask(this.scheduleCronTask(task));
}
}
IntervalTask task;
if (this.fixedRateTasks != null) {
var1 = this.fixedRateTasks.iterator();
while(var1.hasNext()) {
task = (IntervalTask)var1.next();
this.addScheduledTask(this.scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
var1 = this.fixedDelayTasks.iterator();
while(var1.hasNext()) {
task = (IntervalTask)var1.next();
this.addScheduledTask(this.scheduleFixedDelayTask(task));
}
}
}
此部分只是对原理进行了简单的介绍,如果有兴趣深入了解,可以去看看源码~
做定时任务还可以使用java自带的原生API,Timer和TimerTask去设计。
可以这样理解Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,而TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。
这里就简单的提一下,并不是本文的重点,具体的用法自行google吧~
如果转载此博文,请附上本文链接:https://blog.csdn.net/CSDN___LYY/article/details/85266567 谢谢合作~ 如果感觉这篇文章对您有所帮助,请点击一下喜欢或者关注博主,您的喜欢和关注将是我前进的最大动力!