@Import(SchedulingConfiguration.class)
public @interface EnableScheduling {
}
class SchedulingConfiguration {
/**
* 注册一个beanName为internalScheduledAnnotationProcessor的后置处理器
*
* @return
*/
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
/**
* 处理异步的后置处理器
*/
class ScheduledAnnotationBeanPostProcessor implements
ScheduledTaskHolder,
MergedBeanDefinitionPostProcessor,
// 还是一个监听器,监听ContextRefreshe事件
ApplicationListener<ContextRefreshedEvent>
SmartInitializingSingleton {
// 任务的注册表,可以看下面的类介绍
private final ScheduledTaskRegistrar registrar;
// 当前需要执行的任务集合
// bean -> beanMethod(Tasks)
private final Map<Object, Set<ScheduledTask>> scheduledTasks = new IdentityHashMap<>(16);
// 执行任务的调度器
private Object scheduler;
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 当前Bean是Aop相关的处理类,或者是任务调度器,或者是线程池,则不处理
// 因为我们实际上只需要处理普通的类就行
if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler || bean instanceof ScheduledExecutorService) {
return bean;
}
// 获取类的实际处理类型
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
// 查询缓存,当前目标类是不是没有@Schedule注解,处理过的会被缓存起来
// 判断该类是否符合候选条件,isCandidateClass这个方法就是简单校验一下类不能是JDK内部的类或者Ordered接口,其他的一律都符合
if (!this.nonAnnotatedClasses.contains(targetClass) && AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
// 找到类中的所有方法
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
// 找到方法中的Scheduled注解,因为Scheduleds可以包裹多个,所以在一个方法中可以存在多个被Scheduleds包裹的Scheduled
Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);
// 返回找到的方法
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
});
// 如果不存在注解的方法
if (annotatedMethods.isEmpty()) {
// 缓存该类被处理过,该类没有注解
this.nonAnnotatedClasses.add(targetClass);
} else {
// 存在有注解的方法
// 执行任务,详见processScheduled方法
annotatedMethods.forEach((method, scheduledMethods) -> scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
}
}
return bean;
}
// 执行任务
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
Runnable runnable = createRunnable(bean, method) {
// 校验方法是否可用
Method invocableMethod = AopUtils.selectInvocableMethod(method, target.getClass()) {
// 找到可用的方法对象,可能在接口上,可能在类上
Method methodToUse = MethodIntrospector.selectInvocableMethod(method, targetType);
// 该类为代理类,并且方法为非静态的私有方法,就不可用,因为代理类是无法调用目标类的非静态私有方法
if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) && SpringProxy.class.isAssignableFrom(targetType)) {
throw new IllegalStateException(String.format(method.getName(), method.getDeclaringClass().getSimpleName()));
}
}
// 将目标对象和方法封装成runable
return new ScheduledMethodRunnable(target, invocableMethod);
}
// 有任务需要执行的的标识
boolean processedSchedule = false;
// 异常提示
String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
// 执行的任务
Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
// 第一步,确定设置了initialDelay属性的值,只为int
long initialDelay = scheduled.initialDelay();
// 获取initialDelayString的值默认为"",这两个属性不能同时设置,它可以使用表达式${}
String initialDelayString = scheduled.initialDelayString();
// 如果设置了初始的延迟时间
if (StringUtils.hasText(initialDelayString)) {
// 两者同时设置,抛出异常
Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
// 使用占位符解析器对字符串进行解析
if (this.embeddedValueResolver != null) {
initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
}
// 如果解析了占位符的值,就将值转换为Long,解析失败,抛出异常
if (StringUtils.hasLength(initialDelayString)) {
initialDelay = parseDelayAsLong(initialDelayString);
}
}
// 第二步: 检查cron表达式
String cron = scheduled.cron();
// 存在cron表达式
if (StringUtils.hasText(cron)) {
// 将为其解析cron表达式的时区
String zone = scheduled.zone();
// 使用占位符解析器对cron和cron解析成具体的值
if (this.embeddedValueResolver != null) {
cron = this.embeddedValueResolver.resolveStringValue(cron);
zone = this.embeddedValueResolver.resolveStringValue(zone);
}
// 如果存在cron
if (StringUtils.hasLength(cron)) {
// 校验,cron表达式不支持延迟初始化
Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
// 校验通过,表示有任务可以执行
processedSchedule = true;
// cron为"-"表示不处理这个cron表达式任务
if (!Scheduled.CRON_DISABLED.equals(cron)) {
// 获取到时区
TimeZone timeZone;
if (StringUtils.hasText(zone)) {
timeZone = StringUtils.parseTimeZoneString(zone);
} else {
timeZone = TimeZone.getDefault();
}
// 将cron和时区封装成CronTrigger触发器
// 将任务和触发器封装成CronTask任务对象
// 将任务注册到注册表中
// 返回一个可调度的任务对象ScheduledTask
ScheduledTask scheduledTask = this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone)))
{
// 先判断这个任务是否已经添加过,但是还没被处理
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
// 是否是新任务
boolean newTask = false;
// 如果没添加过
if (scheduledTask == null) {
// 封装成可调度的任务对象
scheduledTask = new ScheduledTask(task);
// 标识着这个任务为新任务
newTask = true;
}
// 存在调度的线程池
if (this.taskScheduler != null) {
// 使用线程池执行
scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
} else {
// 如果不存在调度的线程池
// 先保存调度任务
addCronTask(task) {
this.cronTasks.add(task);
}
// 标识着该任务还没被执行
this.unresolvedTasks.put(task, scheduledTask);
}
// 如果是新任务,返回此任务,如果已存在,返会null
return (newTask ? scheduledTask : null);
}
// 添加到任务列表中
tasks.add(scheduledTask);
}
}
}
// 此时我们不再需要区分初始延迟是否设置
if (initialDelay < 0) {
initialDelay = 0;
}
// 检查固定延时执行时间
// 在上一次调用结束和下一次调用开始之间以固定的毫秒间隔执行带注释的方法。
long fixedDelay = scheduled.fixedDelay();
if (fixedDelay >= 0) {
// 如果上面已经有cron任务,抛出异常
Assert.isTrue(!processedSchedule, errorMessage);
// 标识有执行的任务
processedSchedule = true;
// 将任务以及初始化时间,延迟时间封装成固定延迟任务对象
// 将任务注册到注册表中
ScheduledTask scheduledTask = this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay))
{
// 先判断这个任务是否已经添加过,但是还没被处理
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
// 是否是新任务
boolean newTask = false;
// 如果没添加过
if (scheduledTask == null) {
// 封装成可调度的任务对象
scheduledTask = new ScheduledTask(task);
// 标识着这个任务为新任务
newTask = true;
}
// 存在调度的线程池
if (this.taskScheduler != null) {
// 如果存入初始化延迟
if (task.getInitialDelay() > 0) {
// 获取第一次执行的时间
Date startTime = new Date(System.currentTimeMillis() + task.getInitialDelay());
// 提交任务,执行固定延迟时间任务
scheduledTask.future = this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getInterval());
} else {
scheduledTask.future = this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getInterval());
}
} else {
// 如果不存在调度的线程池
// 先保存调度任务
addFixedDelayTask(task) {
this.fixedDelayTasks.add(task);
}
// 标识着该任务还没被执行
this.unresolvedTasks.put(task, scheduledTask);
}
// 如果是新任务,返回此任务,如果已存在,返会null
return (newTask ? scheduledTask : null);
}
// 添加到任务列表中
tasks.add(scheduledTask);
}
// 获取固定时间的字符串形式,有占位符
String fixedDelayString = scheduled.fixedDelayString();
// 如果设置了值,和fixedDelay几乎一样,只是这个值的获取形式不一样,一个是固定的,一个是动态的
if (StringUtils.hasText(fixedDelayString)) {
// 使用占位符解析器进行解析
if (this.embeddedValueResolver != null) {
// 解析到具体的值
fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
}
// 如果存在具体的值
if (StringUtils.hasLength(fixedDelayString)) {
// 上面必须不存在任务,如果存在,抛出异常
Assert.isTrue(!processedSchedule, errorMessage);
// 标识有执行的任务
processedSchedule = true;
// 将值转换为Long
fixedDelay = parseDelayAsLong(fixedDelayString);
// 和fixedDelay的逻辑一样,只是值的表示形式不一样,一个是固定值,一个是${}可读取配置的值
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
}
}
// 在调用之间以固定的毫秒间隔执行方法
long fixedRate = scheduled.fixedRate();
if (fixedRate >= 0) {
// 如果上面已经有任务,抛出异常
Assert.isTrue(!processedSchedule, errorMessage);
// 标识有执行的任务
processedSchedule = true;
// 将任务以及初始化时间,延迟时间封装成固定频率任务对象
// 将任务注册到注册表中
ScheduledTask scheduledTask = this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay))
{
// 先判断这个任务是否已经添加过,但是还没被处理
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
// 是否是新任务
boolean newTask = false;
// 如果没添加过
if (scheduledTask == null) {
// 封装成可调度的任务对象
scheduledTask = new ScheduledTask(task);
// 标识着这个任务为新任务
newTask = true;
}
// 存在调度的线程池
if (this.taskScheduler != null) {
if (task.getInitialDelay() > 0) {
// 获取第一次执行的时间
Date startTime = new Date(System.currentTimeMillis() + task.getInitialDelay());
// 调度任务
scheduledTask.future = this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getInterval());
} else {
scheduledTask.future = this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getInterval());
}
} else {
// 如果不存在调度的线程池
// 先保存调度任务
addFixedRateTask(task) {
this.fixedRateTasks.add(task);
}
// 标识着该任务还没被执行
this.unresolvedTasks.put(task, scheduledTask);
}
return (newTask ? scheduledTask : null);
}
// 添加到任务列表中
tasks.add(scheduledTask);
}
// 获取固定频率的字符串形式,有占位符
String fixedRateString = scheduled.fixedRateString();
// 如果设置了值,和fixedRate几乎一样,只是这个值的获取形式不一样,一个是固定的,一个是动态的
if (StringUtils.hasText(fixedRateString)) {
// 使用占位符解析器进行解析
if (this.embeddedValueResolver != null) {
// 解析到具体的值
fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
}
// 如果存在具体的值
if (StringUtils.hasLength(fixedRateString)) {
// 上面必须不存在任务,如果存在,抛出异常
Assert.isTrue(!processedSchedule, errorMessage);
// 标识有执行的任务
processedSchedule = true;
// 将值转换为Long
fixedRate = parseDelayAsLong(fixedRateString);
// 和fixedRate的逻辑一样,只是值的表示形式不一样,一个是固定值,一个是${}可读取配置的值
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
}
}
// 校验必须存在可以执行的任务
Assert.isTrue(processedSchedule, errorMessage);
// 最后,注册任务
synchronized (this.scheduledTasks) {
// 使用当前bean作为Key,缓存这个对象中所有存在的任务
Set<ScheduledTask> regTasks = this.scheduledTasks.computeIfAbsent(bean, key -> new LinkedHashSet<>(4));
regTasks.addAll(tasks);
}
}
// 监听事件回调
public void onApplicationEvent(ContextRefreshedEvent event) {
// 如果是同一个上下文件的事件
if (event.getApplicationContext() == this.applicationContext) {
// 处理完成注册任务之后的逻辑
finishRegistration() {
// 如果当前后置处理器设置了调度器,将调度器给任务注册表
if (this.scheduler != null) {
this.registrar.setScheduler(this.scheduler);
}
//
if (this.beanFactory instanceof ListableBeanFactory) {
// 从容器中获取SchedulingConfigurer类型的接口
Map<String, SchedulingConfigurer> beans = ((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
// 对这些SchedulingConfigurer进行排序
List<SchedulingConfigurer> configurers = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(configurers);
// 回调SchedulingConfigurer的configureTasks,将任务注册表给它
// 就可以动态的处理任务了
// 所以,我们需要动态添加任务,就可以实现SchedulingConfigurer接口,获取任务注册表,操作任务
for (SchedulingConfigurer configurer : configurers) {
configurer.configureTasks(this.registrar);
}
}
// 如果注册表中有任务,并且没有设置线程池调度器
if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
try {
// resolveSchedulerBean: 获取TaskScheduler类型的Bean对象,并且维护对象的依赖关系
// false: 表示使用类型获取对应的Bean
this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false));
} catch (NoUniqueBeanDefinitionException ex) {
// resolveSchedulerBean: 获取Bean对象,并且维护对象的依赖关系
// true: 表示通过beanName获取Bean,默认的beanName为"taskScheduler"
this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, true));
} catch (NoSuchBeanDefinitionException ex) {
try {
// 如果没有,那么通过类型获取ScheduledExecutorService类型的Bean
// false: 表示使用类型获取对应的Bean
this.registrar.setScheduler(resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, false));
} catch (NoUniqueBeanDefinitionException ex2) {
// 如果还是没有,那么通过beanName获取ScheduledExecutorService类型的Bean
// true: 表示通过beanName获取Bean,默认的beanName为"taskScheduler"
this.registrar.setScheduler(resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, true));
}
}
}
// 执行任务注册表的初始化逻辑
this.registrar.afterPropertiesSet() {
// 进行任务调度,查看下面ScheduledTaskRegistrar的scheduleTasks方法
scheduleTasks();
}
}
}
}
}
class ScheduledTaskRegistrar implements ScheduledTaskHolder, InitializingBean {
// 任务调度器
private TaskScheduler taskScheduler;
// 本地的线程池
private ScheduledExecutorService localExecutor;
// TriggerTask类型的任务
private List<TriggerTask> triggerTasks;
// Cron类型的任务
private List<CronTask> cronTasks;
// 固定周期性的任务
private List<IntervalTask> fixedRateTasks;
// 固定延迟性的任务
private List<IntervalTask> fixedDelayTasks;
// 为被调度的任务集合
private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<>(16);
// 被调度的任务集合
private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet<>(16);
// 初始化
public void afterPropertiesSet() {
// 调度所有任务
scheduleTasks();
}
// 调度任务
protected void scheduleTasks() {
// 如果不在执行任务的线程池调度器
if (this.taskScheduler == null) {
// 创建一个线程池
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
// 创建一个调度器,封装线程池
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
// 如果存在TriggerTask
if (this.triggerTasks != null) {
// 遍历所有的TriggerTask,这种任务是手动添加的,@Schedule注解不会产生这种任务
for (TriggerTask task : this.triggerTasks) {
// scheduleTriggerTask: 调度任务,上面有讲解,逻辑就是从未执行的任务列表unresolvedTasks中获取任务
// 如果存在任务,并且存在调度器,表示该任务可以立即被调度
// 如果不存在任务,则将任务包装成ScheduledTask类型,再判断是否存在调度器,如果存在,立即调度,如果不存在,保存任务,等存在调度器的时候再进行调度
// 保存已经被调度的任务
addScheduledTask(scheduleTriggerTask(task)) {
// 保存需要调度的任务,为什么要判断为null,因为在scheduleTriggerTask中,如果返回null,就表示该任务被调度过或者已经被保存过
// 然而,在这里,能调scheduleTasks这个方法,上面就一定存在调度器,如果为null,就确定该任务被调度过
if (task != null) {
this.scheduledTasks.add(task);
}
}
}
}
// 下面的这几种任务和上面的逻辑是完全一样的
if (this.cronTasks != null) {
for (CronTask task : this.cronTasks) {
addScheduledTask(scheduleCronTask(task));
}
}
if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) {
addScheduledTask(scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) {
addScheduledTask(scheduleFixedDelayTask(task));
}
}
}
}
Spring中@Schedule注解实现原理
最新推荐文章于 2024-07-12 15:51:39 发布