最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中@EnableScheduling注解的工作原理詳解

 更新時間:2024年01月25日 09:49:43   作者:安迪源文  
這篇文章主要介紹了Spring中@EnableScheduling注解的工作原理詳解,@EnableScheduling是 Spring Framework 提供的一個注解,用于啟用Spring的定時任務(Scheduling)功能,需要的朋友可以參考下

概述

  1. 開發(fā)人員使用注解 @EnableScheduling;
  2. 注解@EnableScheduling導入SchedulingConfiguration;
  3. SchedulingConfiguration定義基礎設施bean ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor;
  4. ScheduledAnnotationBeanPostProcessor在容器啟動時做如下事情
    • 登記所有使用@Scheduled注解的bean方法到一個ScheduledTaskRegistrar,供調(diào)度任務執(zhí)行器TaskScheduler執(zhí)行。
    • 為ScheduledTaskRegistrar指定任務執(zhí)行器TaskScheduler,該任務執(zhí)行器來自容器中的bean TaskScheduler/ScheduledExecutorService(如果不指定,ScheduledTaskRegistrar自己會本地創(chuàng)建一個ConcurrentTaskScheduler)
    • 告訴ScheduledTaskRegistrar將所注冊的調(diào)度任務,也就是使用@Scheduled注解的bean方法,調(diào)度到任務執(zhí)行器TaskScheduler執(zhí)行。

詳細分析

開發(fā)人員注解@EnableScheduling

@EnableScheduling // <==
@SpringBootApplication
public class Application {
    public static void main(String[] args)  {
        SpringApplication.run(Application.class, args);
    }
}

@EnableScheduling注解導入配置@SchedulingConfiguration

@Import(SchedulingConfiguration.class)  // <==
public @interface EnableScheduling {
}

SchedulingConfiguration 定義bean scheduledAnnotationProcessor

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

    // Bean 名稱使用 : 
    // org.springframework.context.annotation.internalScheduledAnnotationProcessor
	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE) // 定義為基礎設施bean
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
		return new ScheduledAnnotationBeanPostProcessor();
	}

}

ScheduledAnnotationBeanPostProcessor#postProcessAfterInitialization檢測處理每個@Scheduled注解的方法 ScheduledAnnotationBeanPostProcessor實現(xiàn)了DestructionAwareBeanPostProcessor,BeanPostProcessor等接口。作為一個BeanPostProcessor,ScheduledAnnotationBeanPostProcessor會針對每個bean的創(chuàng)建,在bean生命周期方法#postProcessAfterInitialization中,掃描該bean中使用了注解@Scheduled的方法,

@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler ||
				bean instanceof ScheduledExecutorService) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}
		Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
       // this.nonAnnotatedClasses 是一個緩存,用于記錄處理過程中所發(fā)現(xiàn)的不包含任何被@Scheduled注解的方法的類
		if (!this.nonAnnotatedClasses.contains(targetClass)) {
          // 獲取類  targetClass 上所有使用注解  @Scheduled 的方法
          // 注意 : 某個方法上可能同時使用多個注解  @Scheduled ,所以以下 annotatedMethods 的每個 Entry 是
          // 一個方法對應一個 @cheduled 集合
			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 (annotatedMethods.isEmpty()) {
              // 如果當前類 targetClass 不包含任何使用注解  @Scheduled 的方法,將其添加到 this.nonAnnotatedClasses
				this.nonAnnotatedClasses.add(targetClass);
				if (logger.isTraceEnabled()) {
					logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
				}
			}
			else {
				// Non-empty set of methods
              // 當前類 targetClass 上找到了使用注解 @Scheduled 的方法,記錄在  annotatedMethods 中,
              // 現(xiàn)在將它們逐個處理,使用的處理為方法 processScheduled             
				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;
	}

#rocessScheduled處理方法上的每個@Scheduled注解,生成一個ScheduledTask并登記到this.scheduledTasks。this.scheduledTasks數(shù)據(jù)結構為 :

  • Map數(shù)據(jù)類型;
    • key是一個對象,其類就是含有方法使用了注解@Scheduled的類;
    • value是一個ScheduledTask集合,方法上的每個注解@Scheduled對應一個ScheduledTask;

實例分析

舉例來講,加入組件MyScheduledTask類中有兩個方法#method1,#method2上一用使用了五個注解Scheduled,則this.scheduledTasks會出現(xiàn)一項針對組件MyScheduledTask bean的項,key是組件MyScheduledTask bean對象自身,value是五個ScheduledTask。

/**
	 * Process the given {@code @Scheduled} method declaration on the given bean.
	 * @param scheduled the @Scheduled annotation
	 * @param method the method that the annotation has been declared on
	 * @param bean the target bean instance
	 * @see #createRunnable(Object, Method)
	 */
	protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
		try {
           // 將使用了 @Scheduled 注解的方法包裝成一個 Runnable 對象  , 隨后構建 ScheduledTask 對象時
           // 會用得到
			Runnable runnable = createRunnable(bean, method);
          // 用于記錄當前 @Scheduled 注解是否已經(jīng)被處理,初始化為 false  
			boolean processedSchedule = false;
			String errorMessage =
			"Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
          // 用于保存針對當前 @Scheduled  注解生成的 ScheduledTask,
          // 該方法完成時,該集合內(nèi)元素數(shù)量通常為 1
			Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
			// Determine initial delay
          // 確定 initial delay 屬性 : 基于注解屬性 initialDelay 或者  initialDelayString 分析得到,
          // 二者只能使用其中之一
			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
          // 檢查這是否是一個 cron 表達式類型的注解  
			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();
						}
	                    // 包裝成為一個 CronTask 
						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;
				// 包裝成為一個 FixedDelayTask 
				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");
					}
	                 // 包裝成為一個 FixedDelayTask    
					tasks.add(this.registrar.scheduleFixedDelayTask(
						new FixedDelayTask(runnable, fixedDelay, initialDelay)));
				}
			}
			// Check fixed rate
           // 檢查這是否是一個固定周期執(zhí)行類型的注解    
			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");
					}
					// 包裝成為一個 FixedRateTask       
					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());
		}
	}

經(jīng)過ScheduledAnnotationBeanPostProcessor以上這些處理,每個bean中所包含的@Scheduled注解都被發(fā)現(xiàn)了,這樣的每條信息最終對應生成一個ScheduledTask,該ScheduledTask會被ScheduledTaskRegistrar registrar登記調(diào)度。這意味著該ScheduledTask從此刻起在程序運行期間就會按照@Scheduled注解所設定的時間點被執(zhí)行。

到此這篇關于Spring中@EnableScheduling注解的工作原理詳解的文章就介紹到這了,更多相關@EnableScheduling注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java之JsonArray用法講解

    Java之JsonArray用法講解

    這篇文章主要介紹了Java之JsonArray用法講解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • java操作mongodb實現(xiàn)CURD功能實例

    java操作mongodb實現(xiàn)CURD功能實例

    mongodb支持多種語言,并且提供了多種語言的驅動,本文使用java操作mongodb實現(xiàn)CURD功能,大家參考使用吧
    2013-12-12
  • 詳解基于Spring Cloud幾行配置完成單點登錄開發(fā)

    詳解基于Spring Cloud幾行配置完成單點登錄開發(fā)

    這篇文章主要介紹了詳解基于Spring Cloud幾行配置完成單點登錄開發(fā),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • Java的stream流多個字段排序的實現(xiàn)

    Java的stream流多個字段排序的實現(xiàn)

    本文主要介紹了Java的stream流多個字段排序的實現(xiàn),主要是兩種方法,第一種是固定多個字段排序和第二種動態(tài)字段進行排序,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • Java實現(xiàn)Excel通用異步導出框架方式

    Java實現(xiàn)Excel通用異步導出框架方式

    我最近要做一個需求,就是設計一個通用的異步導出框架,如果數(shù)據(jù)量小于5W那么直接進行導出,數(shù)據(jù)大于5W則創(chuàng)建異步導出任務,并且前端可以刷新該任務進行,然后后面所有相關功能的導出都使用此框架進行操作,思來想去實現(xiàn)后,想著該需求應該符合大部分實際工作的場景,特此分享!
    2025-10-10
  • 詳解SpringMVC驗證框架Validation特殊用法

    詳解SpringMVC驗證框架Validation特殊用法

    本篇文章主要介紹了詳解SpringMVC驗證框架Validation特殊用法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • 在java中使用dom4j解析xml(示例代碼)

    在java中使用dom4j解析xml(示例代碼)

    鑒于目前的趨勢,我們這里來講講Dom4j的基本用法,不涉及遞歸等復雜操作。Dom4j的用法很多,官網(wǎng)上的示例有那么點兒晦澀,這里就不寫了
    2013-10-10
  • 支持SpEL表達式的自定義日志注解@SysLog介紹

    支持SpEL表達式的自定義日志注解@SysLog介紹

    這篇文章主要介紹了支持SpEL表達式的自定義日志注解@SysLog,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • springboot多模塊項目mvn打包遇到存在依賴但卻無法發(fā)現(xiàn)符號問題

    springboot多模塊項目mvn打包遇到存在依賴但卻無法發(fā)現(xiàn)符號問題

    在SpringBoot多模塊項目中,如果遇到依賴存在但無法發(fā)現(xiàn)符號的問題,常見原因可能是pom.xml配置問題,例如,如果某個模塊僅作為依賴而不是啟動工程,不應在其pom中配置spring-boot-maven-plugin插件,因為這將影響jar包的生成方式
    2024-09-09
  • Java集合排序規(guī)則接口Comparator用法解析

    Java集合排序規(guī)則接口Comparator用法解析

    這篇文章主要介紹了Java集合排序規(guī)則接口Comparator用法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09

最新評論

宜都市| 衡东县| 长治县| 宁夏| 汽车| 彭水| 达孜县| 裕民县| 洛宁县| 繁峙县| 诸暨市| 皮山县| 区。| 十堰市| 和平区| 无棣县| 平舆县| 军事| 文山县| 连山| 米林县| 祥云县| 张掖市| 富民县| 静宁县| 昌都县| 织金县| 德化县| 达拉特旗| 泰兴市| 潜江市| 正镶白旗| 佛坪县| 龙泉市| 苗栗县| 西华县| 贡山| 兰溪市| 且末县| 延寿县| 吉安市|