当前位置:网站首页>@Scheduled source code analysis
@Scheduled source code analysis
2022-07-25 14:56:00 【zulj131】
List of articles
@Scheduled The source code parsing
The analysis part
The basis of scheduled task scheduling is ScheduledAnnotationBeanPostProcessor class , This is an implementation BeanPostProcessor The postprocessor of the interface .
About BeanPostProcessor, The most important thing is to see postProcessBeforeInitialization Methods and postProcessAfterInitialization What logic does the method do .
postProcessBeforeInitialization Method does not implement logic , So look postProcessAfterInitialization The logic of the method .
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
/** * Omit part of the code above , Look at the key code below */
if (!this.nonAnnotatedClasses.contains(targetClass) &&
AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
/** * Here is a long string of code to get the information that is @Scheduled and @Schedules Method of annotation */
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
method, Scheduled.class, Schedules.class);
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
});
// If not @Scheduled and @Schedules Method of annotation , At present bean Add to nonAnnotatedClasses Collection , No processing
if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetClass);
if (logger.isTraceEnabled()) {
logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
}
}
else {
// If there is a being @Scheduled and @Schedules Method of annotation , For each method call processScheduled Method
annotatedMethods.forEach((method, scheduledMethods) ->
scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
if (logger.isTraceEnabled()) {
logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
"': " + annotatedMethods);
}
}
}
return bean;
}
According to the above code , Sum up ScheduledAnnotationBeanPostProcessor Class does :
(1) Get the quilt @Scheduled and @Schedules How to annotate and mark , If there is no , Put this Bean Add to nonAnnotatedClasses Collection .
(2) Being is @Scheduled and @Schedules Method of annotation , For each method call processScheduled Method
therefore , Next is the analysis. The key is processScheduled The logic of method
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
try {
// Encapsulate the annotated method as ScheduledMethodRunnable
Runnable runnable = createRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage =
"Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
// analysis initialDelay Value , String and integer values cannot be configured at the same time
long initialDelay = scheduled.initialDelay();
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);
}
if (StringUtils.hasLength(initialDelayString)) {
try {
initialDelay = parseDelayAsLong(initialDelayString);
}
catch (RuntimeException ex) {
throw new IllegalArgumentException(
"Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into long");
}
}
}
// Check cron expression
String cron = scheduled.cron();
if (StringUtils.hasText(cron)) {
String zone = scheduled.zone();
if (this.embeddedValueResolver != null) {
cron = this.embeddedValueResolver.resolveStringValue(cron);
zone = this.embeddedValueResolver.resolveStringValue(zone);
}
if (StringUtils.hasLength(cron)) {
Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
processedSchedule = true;
if (!Scheduled.CRON_DISABLED.equals(cron)) {
TimeZone timeZone;
if (StringUtils.hasText(zone)) {
timeZone = StringUtils.parseTimeZoneString(zone);
}
else {
timeZone = TimeZone.getDefault();
}
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
}
}
}
// At this point we don't need to differentiate between initial delay set or not anymore
if (initialDelay < 0) {
initialDelay = 0;
}
// Check fixed delay
long fixedDelay = scheduled.fixedDelay();
if (fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
}
String fixedDelayString = scheduled.fixedDelayString();
if (StringUtils.hasText(fixedDelayString)) {
if (this.embeddedValueResolver != null) {
fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
}
if (StringUtils.hasLength(fixedDelayString)) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
try {
fixedDelay = parseDelayAsLong(fixedDelayString);
}
catch (RuntimeException ex) {
throw new IllegalArgumentException(
"Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into long");
}
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
}
}
// Check fixed rate
long fixedRate = scheduled.fixedRate();
if (fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
}
String fixedRateString = scheduled.fixedRateString();
if (StringUtils.hasText(fixedRateString)) {
if (this.embeddedValueResolver != null) {
fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
}
if (StringUtils.hasLength(fixedRateString)) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
try {
fixedRate = parseDelayAsLong(fixedRateString);
}
catch (RuntimeException ex) {
throw new IllegalArgumentException(
"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into long");
}
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
}
}
// Check whether we had any attribute set
Assert.isTrue(processedSchedule, errorMessage);
// Finally register the scheduled tasks
synchronized (this.scheduledTasks) {
Set<ScheduledTask> regTasks = this.scheduledTasks.computeIfAbsent(bean, key -> new LinkedHashSet<>(4));
regTasks.addAll(tasks);
}
}
catch (IllegalArgumentException ex) {
throw new IllegalStateException(
"Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
}
}
According to the above code , The general logic is as follows :
(1) analysis initialDelay Value
(2) according to @Scheduled Attribute configuration of annotations , Separate this bean Of is annotated // The method is encapsulated as CronTask,FixedDelayTask,FixedRateTask
(3)this.registrar Use the corresponding scheduling method according to the encapsulated task type scheduleXXX
If at this time taskScheduler Local variables have not been initialized , Then it will be added to a temporary collection and saved , No scheduling , This taskScheduler It can be seen as a thread pool dedicated to scheduling tasks
(4) The result returned by the scheduling method is added to tasks Collection
(5) And then according to bean classification , Put in scheduledTasks aggregate ( With bean by key Of Map aggregate )
among @Scheduled annotation The restrictions are as follows :
(1)cron Expressions cannot be associated with initialDelay,fixedDelay,fixedRate Configure together
(2)fixedDelay Cannot be associated with cron Simultaneous setting
(3)fixedRate Cannot be associated with cron Simultaneous configuration
(4)fixedDelay and fixedRate Cannot be configured at the same time
thus postProcessAfterInitialization Method execution complete .
To sum up , This method is to put @Scheduled The annotation method is parsed out , Then convert to ScheduledTask, This is probably the object that represents a scheduled task , And then according to the bean Group and store in a Map Collection .
Executive part
After verification , Actually, it's being implemented postProcessAfterInitialization Method ,taskScheduler Still for null Of , in other words , In fact, there is still no way to start scheduling and execution of each scheduled task .
for instance :
@Nullable
public ScheduledTask scheduleFixedRateTask(FixedRateTask task) {
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.unresolvedTasks.put(task, scheduledTask);
}
return (newTask ? scheduledTask : null);
}
At this time due to taskScheduler by null, Therefore, it was not implemented this.taskScheduler.scheduleAtFixedRate Method , It's called addFixedRateTask(task).( After testing , Even if you customize taskScheduler, It won't be assigned at this time )
The above this.taskScheduler.scheduleAtFixedRate Method In what execution ? With this question , Debugging and management , Finally found in onApplicationEvent Method before it executes scheduling , here taskScheduler Not empty .
ScheduledAnnotationBeanPostProcessor Class implements the ApplicationListener Interface , monitor ContextRefreshedEvent event . According to the previous study Spirng Loading process ,ContextRefreshedEvent Event is Spring After the container is loaded , perform finishRefesh Method .
The monitoring method mainly implements finishRegistration() Method
private void finishRegistration() {
// fragment 1
if (this.beanFactory instanceof ListableBeanFactory) {
Map<String, SchedulingConfigurer> beans =
((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
List<SchedulingConfigurer> configurers = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(configurers);
for (SchedulingConfigurer configurer : configurers) {
configurer.configureTasks(this.registrar);
}
}
// Omit some codes ....
this.registrar.afterPropertiesSet();
}
There's a place , What I think is worth knowing :
fragment 1: When customizing the scheduling thread pool SchedulingConfigurer Interface Of configureTasks Method , This method is in the segment 1 Executive .
Then later, the more important . It mainly depends on this.registrar.afterPropertiesSet Method
this.registrar.afterPropertiesSet Called in the method scheduleTasks() Method
protected void scheduleTasks() {
// fragment 1
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
// fragment 2
if (this.triggerTasks != null) {
for (TriggerTask task : this.triggerTasks) {
addScheduledTask(scheduleTriggerTask(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));
}
}
}
fragment 1:this.taskScheduler If null, Then use Executors.newSingleThreadScheduledExecutor(). If it is a custom thread pool , It won't be implemented , Because it has been assigned at this time .
fragment 2: According to different types of scheduled tasks , Call different schedules respectively API
there this.cronTasks,this.fixedRateTasks,this.fixedDelayTasks It is implemented above processScheduled When the method is used , because this.taskScheduler by null And the temporary storage place for scheduled tasks .
Because now there are this.taskScheduler , Therefore, they are officially added to the scheduling , And put in scheduledTasks Collection ( Those who have participated in the scheduling will not be added repeatedly ).
Summary
(1) From this source code analysis , You can know by implementing SchedulingConfigurer Interface to customize the configuration of scheduling thread pool
(2)@Scheduled annotation The limitation of , Multiple task types cannot be configured at the same time
边栏推荐
- LeetCode-198-打家劫舍
- 44 Sina navigation, Xiaomi sidebar exercise
- 物理量与单位符号的书写标准
- Writing standard of physical quantities and unit symbols
- Add the jar package under lib directory to the project in idea
- Jmeter的随机数函数怎么用
- Leetcode-198- house raiding
- awk从入门到入土(20)awk解析命令行参数
- BigDecimal rounds the data
- 37 元素模式(行内元素,块元素,行内块元素)
猜你喜欢

The solution to the problem that the progress bar of ros2 installation connext RMW is stuck at 13%

L1和L2正则化

Development of uni app offline ID card identification plug-in based on paddleocr

牛客多校 E G J L

"How to use" decorator mode

IP地址分类,判断一个网段是子网超网

51 single chip microcomputer learning notes (1)

Melody + realsense d435i configuration and error resolution

44 新浪导航 ,小米边栏 练习

IP address classification, which determines whether a network segment is a subnet supernetwork
随机推荐
awk从入门到入土(23)awk内置变量ARGC、ARGC--命令行参数传递
Quickly set up dobbo demo
物理量与单位符号的书写标准
35 快速格式化代码
easygui使用的语法总结
微信公众号正式环境上线部署,第三方公众平台接入
45padding won't open the box
Jmeter的随机数函数怎么用
[C topic] Li Kou 88. merge two ordered arrays
44 Sina navigation, Xiaomi sidebar exercise
How to use the random number function of JMeter
35 quick format code
Ten common application scenarios of redis
"How to use" agent mode
Software testing -- 1. Outline of software testing knowledge
H5页面input输入框弹起数字键盘,需要支持小数点
【MySQL必知必会】触发器 | 权限管理
C language and SQL Server database technology
IP address classification, which determines whether a network segment is a subnet supernetwork
Gameframework making games (I)