前言
在这个竞争异常激烈的社会,每个人或多或少都有着不一样的"病",就拿我来说吧,我有严重的强迫症,东西没有摆放整齐,门要锁好几遍,定闹钟要确认好几遍才能入睡等等,随着年龄的增长,记忆力也衰退了,记不住事儿。
但是老是记不住事儿也不是个办法啊,就像上班打卡,日常工作安排,都是不能忘记的,所以我把Iphone上的"提醒事项这个功能",闹钟呢也设定的"就寝"功能,第一个是用于日常要记住的东西,当我设定时间和任务之后,它会在指定的时间提醒我,再者呢"就寝"会分析我的作息,并给我安排一个合理的作息时间,每天都会重复在指定时间闹醒我,省了不少事儿~
那这些功能统称为我们的定时任务!
何为Quartz
如果我们想在Spring中使用定时任务管理,就不得不提到Quartz了,那什么是Quartz呢?大家先看看它的介绍
Quartz简介:
在企业应用中,我们经常会碰到时间任务调度的需求,比如每天凌晨生成前天报表,每小时生成一次汇总数据等等。Quartz是出了名的任务调度框架,它可以与J2SE和J2EE应用程序相结合,功能灰常强大,轻轻松松就能与Spring集成,使用方便。
Quartz中的概念:
Quartz有三个核心概念:调度器、任务和触发器。三者关系简单来说就是,调度器负责调度各个任务,到了某个时刻或者过了一定时间,触发器触动了,特定任务便启动执行。概念相对应的类和接口有:
JobDetail(任务):望文生义就是描述任务的相关情况;
Trigger(触发器):描述出发Job执行的时间触发规则。有SimpleTrigger和CronTrigger两个子类代表两种方式,一种是每隔多少分钟小时执行,则用SimpleTrigger;另一种是日历相关的重复时间间隔,如每天凌晨,每周星期一运行的话,通过Cron表达式便可定义出复杂的调度方案。
Scheduler(调度器):代表一个Quartz的独立运行容器,Trigger和JobDetail要注册到Scheduler中才会生效,也就是让调度器知道有哪些触发器和任务,才能进行按规则进行调度任务。
听到时间的任务和触发,是不是感觉很像JavaScript的Interval事件触发?只不过我们这里我们是在Java中设置定时和触发。Quartz翻译过来就是石英的意思,根据常识,大多数的表都是含有石英成分的,因此Quartz有着时间的含义,那么介绍完Quartz,我们来看看Spring如何启用定时任务吧~
Spring启用Quartz实现定时任务
方式一:基于MethodInvokeJobDetailFactoryBean实现
注意啦,Spring中是没有Quartz jar包的,因此我们需要自己去下载quartz的官方jar包,那么除了之前我们导入过的jar包,我们还需要导入以下三个jar,quartz-2.3.0.jar是使用Quartz的核心jar包,另外两个slf4j和log4j类似,只不过Quartz依赖的是slf4j,因此我们也需要导入,这两个jar可以在mybatis的辅助包中找到。
那么jar包导入之后,还是给大家看看目录结构啦
第一步我们来创建一个"定时闹钟"
public class JobClock {
public void execute() {
System.out.println("叮铃铃~叮铃铃~" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
那闹钟有了,我们就要设置任务或者说让它每隔多久闹一次啦,设置闹钟的任务当然就要交给我们的xml咯
注意了这里我专门建了一个application-task.xml存放相关的任务,因为今后的项目会越来越大,那么不同的xml会有不同的职责,只有明确了自己的职责,各司其职,才方便后期管理。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 创建一个闹钟 -->
<bean id="jobClock" class="com.marco.quartz.JobClock"></bean>
<!-- 创建一个任务对象 -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!-- 设置目标对象-->
<property name="targetObject" ref="jobClock"></property>
<!-- 设置目标方法-->
<property name="targetMethod" value="execute"></property>
</bean>
<!-- 设置触发器,cronTrigger的意思就是时间触发器的意思 -->
<bean id="cronTriggerFactoryBean" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- 把任务加入触发器中-->
<property name="jobDetail" ref="jobDetail"></property>
<!-- 时间表达式,设置每隔5秒触发一次-->
<property name="cronExpression" value="0/5 * * * * ?"></property>
</bean>
<!-- 设置事件调度器,注意只有将Trigger和JobDetail注册到调度器中才能按照规则进行调度-->
<bean id="springJobSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<array>
<ref bean="cronTriggerFactoryBean"/>
</array>
</property>
</bean>
</beans>
接下来将application-task.xml引入到applicationContext.xml就可以啦
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="classpath:application-task.xml"/>
</beans>
那我们来看看运行结果,是不是每隔5秒"闹钟"闹一次呢?
注意了,当这个任务开启之后是没有办法控制它什么时候停止的,除非删除这个任务!
另外呢,关于corn的表达式,可以直接点击浏览这个地址 corn格式转换器,会帮你自动生成你想要的定时规则哦
方式二:基于JobDetailBean实现
那凡事最少有三种解决方式,那么我们来介绍第二种方式,首先需要修改我们的JobClock,继承QuartzJobBean,并重写父类的方法executeInternal(),看源码我们不难看出QuartzJobBean实现了Job接口,也就是说QuartzJobBean是任务中的一种,专门用于设置时间,并重写了execute方法,而execute实际上又执行的是executeInternal也就是我们重写QuartzJobBean的方法
那么分析过它的实现原理之后,我们就来重写QuartzJobBean的方法
public class JobClock extends QuartzJobBean{
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("叮铃铃~叮铃铃~" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
JobClock继承了QuartzJobBean之后,在application-task.xml中就不用再单独声明
<bean id="jobClock" class="com.marco.quartz.JobClock"></bean>
了,我们可以将下面的代码
<bean id="jobClock" class="com.marco.quartz.JobClock"></bean>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="jobClock"></property>
<property name="targetMethod" value="execute"></property>
</bean>
替换为下面的代码,就可以执行我们的任务啦~
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!-- 执行目标job的class地址 -->
<property name="jobClass" value="com.marco.quartz.JobClock" ></property>
<!-- 设置是否重复执行-->
<property name="durability" value="true"></property>
</bean>
方式三:基于注解实现
第三种方式永远都是最简单的,果然注解也没有让我们失望,毕竟从难到易才能凸显它的优势嘛
application-task.xml我们就不要了,重点修改JobClock的代码
@Component//通过IoC注入对象JobClock并放置在容器中
public class JobClock {
@Scheduled(cron="0/5 * * * * ?")
public void execute() {
System.out.println("叮铃铃~叮铃铃~" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
接下来在applicationContext.xml中对com.marco.quartz下面的类进行扫描就可以啦~
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描com.marco.quartz -->
<context:component-scan base-package="com.marco.quartz"></context:component-scan>
</beans
运行一下… 欸?发现好像,没有生效…
咳,不是故意坑你们,加深一下印象,如果想让task生效有两种方式
第一种方式:
在applicationContext.xml文件中加上下面的标签
<task:annotation-driven/>
这个标签是什么意思呢?根据字面意思annotation-driven就是注解驱动,那么前面加上task,那么它的意思就是任务的注解驱动,@Scheduled(cron="0/5 * * * * ?")
就是属于我们任务范畴的,因此只有开启任务注解驱动之后,我们的@Scheduled才会生效
第二种方式:
在JobClock类上注明@EnableScheduling
,那么Spring在扫描这个类的时候,就会默认开启驱动,那么调度器也会生效。
@Component
@EnableScheduling
public class JobClock {
@Scheduled(cron="0/5 * * * * ?")
public void execute() {
System.out.println("叮铃铃~叮铃铃~" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
后语
有的时候不得不感叹人有的时候真的不如机器呀!随着年龄越来越大,记性也会越来越差,可能有一天写着写着我都会忘了前面讲的到底是啥,所以我会更加认真的写博文,不仅大家能够学到东西,可能在未来的某一天,看到这些文章,也能很快回忆起来,或许还会有些感悟吧~