当前位置:   article > 正文

SpringBoot 之 @Scheduled 定时器规则_scheduled 每分钟执行一次

scheduled 每分钟执行一次

原文:https://maiyikai.github.io/2019/12/27/1577427502

项目中,或多或少都会使用到定时器,定时执行某些特殊得功能。而在 SpringBoot 项目中,使用得定时器功能就是使用 @Scheduled 注解。
当然,定时器功能打开了,但是没有定时,这是不可行的。所以,在定时器使用中,使用 Cron 表达式作为定时器规则,在 @Scheduled 注解源码中可以看到,除了 cron 属性之外还有多个功能不一的属性。

Cron 表达式解释入口

这里主要简述的是 Cron 表达式,在版本 1.5.6 版本的 SpringBoot 工程中,解析 Cron 表达式的源码如下(源码地址:org.springframework.scheduling.support.CronTrigger):

	private void parse(String expression) throws IllegalArgumentException {
		String[] fields = StringUtils.tokenizeToStringArray(expression, " ");
		if (fields.length != 6) {
			throw new IllegalArgumentException(String.format(
					"Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));
		}
		setNumberHits(this.seconds, fields[0], 0, 60);
		setNumberHits(this.minutes, fields[1], 0, 60);
		setNumberHits(this.hours, fields[2], 0, 24);
		setDaysOfMonth(this.daysOfMonth, fields[3]);
		setMonths(this.months, fields[4]);
		setDays(this.daysOfWeek, replaceOrdinals(fields[5], "SUN,MON,TUE,WED,THU,FRI,SAT"), 8);
		if (this.daysOfWeek.get(7)) {
			// Sunday can be represented as 0 or 7
			this.daysOfWeek.set(0);
			this.daysOfWeek.clear(7);
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

从以上源码看出,当前版本的 SpringBoot 中,针对于 @ScheduledCron 表达式,只支持 6 位规则,而在网上有资料表示可以存在 7 位。

但是源码中,限制了只能有 6 位,否则抛出异常。

Cron 表达是对应的内容为: seconds minutes hours daysOfMonth months daysOfWeek

域的解释及源码

针对不同属性解释:

  • seconds:秒数;最小值为 0 ; 最大值为 60。
  • minutes:分钟;最小值为 0 ; 最大值为 60。
  • hours:小时;最小值为 0 ; 最大值为 23。
  • daysOfMonth:月中的某一天。
  • months:月份,这里使用的值为:FOO,JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC 其中一个,且必须全部大写。
  • daysOfWeek:一周中的某一天,这里使用的值为:SUN,MON,TUE,WED,THU,FRI,SAT 其中一个,且必须全部大写。

在解释这个规则时,都是用了相同的两个方法,源码如下:

	private void setNumberHits(BitSet bits, String value, int min, int max) {
		String[] fields = StringUtils.delimitedListToStringArray(value, ",");
		for (String field : fields) {
			if (!field.contains("/")) {
				// Not an incrementer so it must be a range (possibly empty)
				int[] range = getRange(field, min, max);
				bits.set(range[0], range[1] + 1);
			}
			else {
				String[] split = StringUtils.delimitedListToStringArray(field, "/");
				if (split.length > 2) {
					throw new IllegalArgumentException("Incrementer has more than two fields: '" +
							field + "' in expression \"" + this.expression + "\"");
				}
				int[] range = getRange(split[0], min, max);
				if (!split[0].contains("-")) {
					range[1] = max - 1;
				}
				int delta = Integer.valueOf(split[1]);
				if (delta <= 0) {
					throw new IllegalArgumentException("Incrementer delta must be 1 or higher: '" +
							field + "' in expression \"" + this.expression + "\"");
				}
				for (int i = range[0]; i <= range[1]; i += delta) {
					bits.set(i);
				}
			}
		}
	}

	private int[] getRange(String field, int min, int max) {
		int[] result = new int[2];
		if (field.contains("*")) {
			result[0] = min;
			result[1] = max - 1;
			return result;
		}
		if (!field.contains("-")) {
			result[0] = result[1] = Integer.valueOf(field);
		}
		else {
			String[] split = StringUtils.delimitedListToStringArray(field, "-");
			if (split.length > 2) {
				throw new IllegalArgumentException("Range has more than two fields: '" +
						field + "' in expression \"" + this.expression + "\"");
			}
			result[0] = Integer.valueOf(split[0]);
			result[1] = Integer.valueOf(split[1]);
		}
		if (result[0] >= max || result[1] >= max) {
			throw new IllegalArgumentException("Range exceeds maximum (" + max + "): '" +
					field + "' in expression \"" + this.expression + "\"");
		}
		if (result[0] < min || result[1] < min) {
			throw new IllegalArgumentException("Range less than minimum (" + min + "): '" +
					field + "' in expression \"" + this.expression + "\"");
		}
		return result;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
特殊字符解释及例子

每个域都使用数字,或者使用连接符连接,含义:

  • , :连接符,表示列出枚举值。如在 minutes 域使用 2,15 ,表示 2 分和 15 分执行一次。
  • - :连接符,表示范围。如在 minutes 域使用 2-15 ,表示从 2 分到 15 分,每分钟执行一次。
  • / :连接符,表示值增加的幅度,如 n/m ,表示从第 n 秒开始,每隔 m 秒执执行一次,5/15 -->> 5, 20, 35, 50
  • * :表示匹配该域的任意值,如在 minutes 域使用,表示每分钟都会触发一次
  • ? :表示匹配该域的任意值,但只有 daysOfMonth 和 daysOfWeek 域才能使用,因为 daysOfMonth 和 daysOfWeek 域会相互影响,所以两个域不能同时使用 ? 。

注:在源码中未使用到的特殊符号有 L (表最后)、 W (表有效工作日)、 LW (表最后某月最后一个工作日)、 # (表用于确定每个月第几个星期几)

一些常用的例子,这里用表格展示:

规则解释
0 0 10,14,16 * * ?每天上午10点,下午2点,4点
0 0/30 9-17 * * ?朝九晚五工作时间内每半小时
0 0 12 ? * WED表示每个星期三中午12点
0 0 12 * * ?每天中午12点触发
0 15 10 ? * *每天上午10:15触发
0 15 10 * * ?每天上午10:15触发
0 * 14 * * ?在每天下午2点到下午2:59期间的每1分钟触发
0 0/5 14 * * ?在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ?在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0-5 14 * * ?在每天下午2点到下午2:05期间的每1分钟触发
0 10,44 14 ? 3 WED每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI周一至周五的上午10:15触发
0 15 10 15 * ?每月15日上午10:15触发
总结

文章所述与网上资料有所不同,需要根据源码判断,是否正确使用。
根据目前 SpringBoot 版本 1.5.6 来看,对于 Cron 表达式的使用相对简单。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/728249
推荐阅读
相关标签
  

闽ICP备14008679号