在作项目时有时候会有定时器任务的功能,好比某某时间应该作什么,多少秒应该怎么样之类的。java
spring支持多种定时任务的实现。咱们来介绍下使用spring的定时器和使用quartz定时器spring
1.咱们使用spring-boot做为基础框架,其理念为零配置文件,全部的配置都是基于注解和暴露bean的方式。数据库
2.使用spring的定时器:并发
spring自带支持定时器的任务实现。其可经过简单配置来使用到简单的定时任务。框架
@Component @Configurable @EnableScheduling public class ScheduledTasks{ @Scheduled(fixedRate = 1000 * 30) public void reportCurrentTime(){ System.out.println ("Scheduling Tasks Examples: The time is now " + dateFormat ().format (new Date ())); } //每1分钟执行一次 @Scheduled(cron = "0 */1 * * * * ") public void reportCurrentByCron(){ System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ())); } private SimpleDateFormat dateFormat(){ return new SimpleDateFormat ("HH:mm:ss"); } }
没了,没错,使用spring的定时任务就这么简单,其中有几个比较重要的注解:maven
@EnableScheduling:标注启动定时任务。ide
@Scheduled(fixedRate = 1000 * 30) 定义某个定时任务。函数
3.使用quartz实现定时任务。
Quartz设计者作了一个设计选择来从调度分离开做业。Quartz中的触发器用来告诉调度程序做业何时触发。框架提供了一把触发器类型,但两个最经常使用的是SimpleTrigger和CronTrigger。SimpleTrigger为须要简单打火调度而设计。典型地,若是你须要在给定的时间和重复次数或者两次打火之间等待的秒数打火一个做业,那么SimpleTrigger适合你。另外一方面,若是你有许多复杂的做业调度,那么或许须要CronTrigger。
CronTrigger是基于Calendar-like调度的。当你须要在除星期六和星期天外的天天上午10点半执行做业时,那么应该使用CronTrigger。正如它的名字所暗示的那样,CronTrigger是基于Unix克隆表达式的。spring-boot
使用quartz说使用的maven依赖。this
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>1.8.4</version> </dependency>
因为咱们使用的是spring-boot框架,其目的是作到零配置文件,因此咱们不使用xml文件的配置文件来定义一个定时器,而是使用向spring容器暴露bean的方式。
向spring容器暴露所必须的bean
@Configuration public class SchedledConfiguration { // 配置中设定了 // ① targetMethod: 指定须要定时执行scheduleInfoAction中的simpleJobTest()方法 // ② concurrent:对于相同的JobDetail,当指定多个Trigger时, 极可能第一个job完成以前, // 第二个job就开始了。指定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成以前开始。 // ③ cronExpression:0/10 * * * * ?表示每10秒执行一次,具体可参考附表。 // ④ triggers:经过再添加其余的ref元素可在list中放置多个触发器。 scheduleInfoAction中的simpleJobTest()方法 @Bean(name = "detailFactoryBean") public MethodInvokingJobDetailFactoryBean detailFactoryBean(ScheduledTasks scheduledTasks){ MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean (); bean.setTargetObject (scheduledTasks); bean.setTargetMethod ("reportCurrentByCron"); bean.setConcurrent (false); return bean; } @Bean(name = "cronTriggerBean") public CronTriggerBean cronTriggerBean(MethodInvokingJobDetailFactoryBean detailFactoryBean){ CronTriggerBean tigger = new CronTriggerBean (); tigger.setJobDetail (detailFactoryBean.getObject ()); try { tigger.setCronExpression ("0/5 * * * * ? ");//每5秒执行一次 } catch (ParseException e) { e.printStackTrace (); } return tigger; } @Bean public SchedulerFactoryBean schedulerFactory(CronTriggerBean[] cronTriggerBean){ SchedulerFactoryBean bean = new SchedulerFactoryBean (); System.err.println (cronTriggerBean[0]); bean.setTriggers (cronTriggerBean); return bean; }
}
MethodInvokingJobDetailFactoryBean:此工厂主要用来制做一个jobDetail,即制做一个任务。因为咱们所作的定时任务根本上讲其实就是执行一个方法。因此用这个工厂比较方便。
注意:其setTargetObject所设置的是一个对象而不是一个类。
CronTriggerBean:定义一个触发器。
注意:setCronExpression:是一个表达式,若是此表达式不合规范,即会抛出异常。
SchedulerFactoryBean:主要的管理的工厂,这是最主要的一个bean。quartz经过这个工厂来进行对各触发器的管理。
4.对quartz的封装
由上面代码能够看出来,此处咱们设置的是一个固定的cronExpression,那么,作为项目中使用的话,咱们通常是须要其动态设置好比从数据库中取出来。
其实作法也很简单,咱们只须要定义一个Trigger来继承CronTriggerBean。顶用其setCronExpression方法便可。
那么另一个问题,若是咱们要定义两个定时任务则会比较麻烦,须要先注入一个任务工厂,在注入一个触发器。
为了减小这样的配置,咱们定义了一个抽象的超类来继承CronTriggerBean。
具体代码以下:
public abstract class BaseCronTrigger extends CronTriggerBean implements Serializable { private static final long serialVersionUID = 1L; public void init(){ // 获得任务 JobDetail jobdetail = new JobDetail (this.getClass ().getSimpleName (),this.getMyTargetObject ().getClass ()); this.setJobDetail (jobdetail); this.setJobName (jobdetail.getName ()); this.setName (this.getClass ().getSimpleName ()); try { this.setCronExpression (this.getMyCronExpression ()); } catch (java.text.ParseException e) { e.printStackTrace (); } } public abstract String getMyCronExpression(); public abstract Job getMyTargetObject(); }
其init()方法,来为这个触发器绑定任务。其任务为一个Job类型的,也就是说其执行的任务为实现了Job接口的类,这个任务会有一个execute()方法,来执行任务题。
public class ScheduledTasks implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException{ System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ())); } private SimpleDateFormat dateFormat(){ return new SimpleDateFormat ("HH:mm:ss"); } }
为了给触发器添加任务,咱们须要在子类中调用init()方法,因为spring容器注入时是使用的空参的构造函数,因此咱们在此构造函数中调用init()方法。
@Component public class InitializingCronTrigger extends BaseCronTrigger implements Serializable { private static final long serialVersionUID = 1L; @Autowired private SchedulerFactoryBean schedulerFactoryBean; public InitializingCronTrigger() { init (); } @Override public String getMyCronExpression(){ return "0/5 * * * * ?"; } @Override public Job getMyTargetObject(){ return new ScheduledTasks (); } public void parse(){ try { schedulerFactoryBean.getObject ().pauseAll (); } catch (SchedulerException e) { e.printStackTrace (); } } }
此时咱们只须要在配置类中加入一个配置就能够了。
@Bean public SchedulerFactoryBean schedulerFactory(CronTriggerBean[] cronTriggerBean){ SchedulerFactoryBean bean = new SchedulerFactoryBean (); System.err.println (cronTriggerBean[0]); bean.setTriggers (cronTriggerBean); return bean; }
4.介绍一个cronExpression表达式。
这一部分是摘抄的:
字段 容许值 容许的特殊字符 秒
0-59
, - * /
分
0-59
, - * /
小时
0-23
, - * /
日期
1-31
, - * / L W C
月份
1-12 或者 JAN-DEC
, - * /
星期
1-7 或者 SUN-SAT
, - * / L C #
年(可选)
留空, 1970-2099
, - * /
如上面的表达式所示:
“*”字符被用来指定全部的值。如:”*“在分钟的字段域里表示“每分钟”。
“-”字符被用来指定一个范围。如:“10-12”在小时域意味着“10点、11点、12点”。
“,”字符被用来指定另外的值。如:“MON,WED,FRI”在星期域里表示”星期1、星期3、星期五”.
“?”字符只在日期域和星期域中使用。它被用来指定“非明确的值”。当你须要经过在这两个域中的一个来指定一些东西的时候,它是有用的。看下面的例子你就会明白。
“L”字符指定在月或者星期中的某天(最后一天)。即“Last ”的缩写。可是在星期和月中“L”表示不一样的意思,如:在月子段中“L”指月份的最后一天-1月31日,2月28日,若是在星期字段中则简单的表示为“7”或者“SAT”。若是在星期字段中在某个value值得后面,则表示“某月的最后一个星期value”,如“6L”表示某月的最后一个星期五。
“W”字符只能用在月份字段中,该字段指定了离指定日期最近的那个星期日。
“#”字符只能用在星期字段,该字段指定了第几个星期value在某月中每个元素均可以显式地规定一个值(如6),一个区间(如9-12),一个列表(如9,11,13)或一个通配符(如*)。“月份中的日期”和“星期中的日期”这两个元素是互斥的,所以应该经过设置一个问号(?)来代表你不想设置的那个字段。表7.1中显示了一些cron表达式的例子和它们的意义:
表达式
意义 "0 0 12 * * ?"
天天中午12点触发
"0 15 10 ? * *"
天天上午10:15触发
"0 15 10 * * ?"
天天上午10:15触发
"0 15 10 * * ? *"
天天上午10:15触发
"0 15 10 * * ? 2005"
2005年的天天上午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触发
"0 15 10 L * ?"
每个月最后一日的上午10:15触发
"0 15 10 ? * 6L"
每个月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005"
2002年至2005年的每个月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3"
每个月的第三个星期五上午10:15触发
天天早上6点 0 6 * * *
每两个小时 0 */2 * * *
晚上11点到早上8点之间每两个小时,早上八点 0 23-7/2,8 * * *
每月的4号和每一个礼拜的礼拜一到礼拜三的早上11点 0 11 4 * 1-3
1月1日早上4点 0 4 1 1 *