当前位置:网站首页>@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
边栏推荐
猜你喜欢

Splice a field of the list set into a single string

51 single chip microcomputer learning notes (1)

06. Neural network like

"Ask every day" how locksupport realizes thread waiting and wakeup
![[MySQL must know and know] trigger | permission management](/img/59/cb805d972097a6a8ed7f3ae454a91d.png)
[MySQL must know and know] trigger | permission management

Browser based split screen reading

I2C device driver hierarchy

Overview of cloud security technology development

32 chrome调试工具的使用

Ssh server rejected password
随机推荐
Add the jar package under lib directory to the project in idea
BIO、NIO、AIO示例
Idea error failed to determine a suitable driver class
51 single chip microcomputer learning notes (1)
各种平台dpkg包下载地址(包括arm64)
MySQL的登陆【数据库系统】
【MySQL必知必会】触发器 | 权限管理
43 box model
C language and SQL Server database technology
Application practice: Great integrator of the paddy classification model [paddlehub, finetune, prompt]
PHP implements non blocking (concurrent) request mode through native curl
Unable to start web server when Nacos starts
基于AMD EPYC服务器的EDA芯片设计解决方案
Is it safe for Guolian securities to buy shares and open an account?
C language and SQL Server database technology
"Ask every day" briefly talk about JMM / talk about your understanding of JMM
Content type corresponding to office file
Melody + realsense d435i configuration and error resolution
I2C设备驱动程序的层次结构
I2C device driver hierarchy