注:文中示例和总结源自结尾的参考博客,感谢各位博主的分享
Quartz是一个开源的,用于处理定时任务的项目,因为相比于Java本身的Timer功能更强大一些,而且和Spring整合在了一起,所以应用广泛。我正好最近接触到的任务中碰到了它,做个记录
Java 定时的四种方式
- 使用Java自带的定时器Timer,自己实现TimerTask(抽象类,扩展了Runnable),传入Timer的方法中,进行调度,缺点是不能满足类似于“在某时某刻执行”的要求,另外可能出现定时不准,因为Timer是单线程的,所以就和JS的定时器一样,存在延迟
- 接着1说,改进是可以通过Java的线程池
ScheduledThreadPoolExecutor
来替代,可以指定多线程,消除定时不准的问题 - 使用Quartz,也就是本文接下来要讲的
- 使用Spring3.0后自带的Task,类似于Quartz,接下来也会介绍
Quartz
使用Quartz分两种:继承QuartzJobBean
和不继承的,二者仅编写定时任务类和xml配置作业类两步不同
使用Quartz的一个注意点是:与Spring3.1以下版本整合必须使用Quartz1,因为Spring3.0版本以下,有一个类继承自Quartz1版本的类,在Quartz更高版本,那个类被改为接口了(见结尾参考博客:Spring与Quartz的整合实现定时任务调度)
使用的步骤分为以下几个:
对于继承QuartzJobBean
,需要做
1. 需要实现定时任务的类继承org.springframework.scheduling.quartz.QuartzJobBean
,实现定时执行的方法,xml里要么声明这个定时任务类,要么使用注解扫描的方式声明
2. xml里声明新的bean,class为org.springframework.scheduling.quartz.JobDetailBean
描述定时任务类,例如:
<bean name="job" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.example.QuartzTimer" /> <!-- 这个属性,指定定时任务类 -->
<property name="jobDataAsMap"><!-- 这个属性,为定时任务类注入属性,注意对应定时任务类,需要有属性的set方法 -->
<map>
<entry key="timeout" value="0" />
</map>
</property>
</bean>
对于不继承,需要做
1. 需要实现定时任务的类,主要是实现定时执行的方法
2. xml里使用MethodInvokingJobDetailFactoryBean
配置作业类,例如
<bean id="job" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<bean class="com.example.QuartzTimer" /> <!-- 这个属性,指定定时任务类 -->
</property>
<property name="targetMethod" value="doJob" />
<property name="concurrent" value="false" /><!-- 作业不并发调度 -->
</bean>
接下来的三步,都一样
3. xml里配置触发器Trigger,用于指定什么时候执行定时任务
触发器分两种:org.springframework.scheduling.quartz.SimpleTriggerBean
和org.springframework.scheduling.quartz.CronTriggerBean
。其中SimpleTriggerBean只支持按频率触发,比如每隔多长时间执行一次,而CronTriggerBean
支持使用Cron表达式,指定类似“在每几天的什么时间执行”的触发
这里需要注意的是,触发器Trigger和定时任务的对应关系:一个触发器只能关联一个定时任务,但是一个定时任务可以关联多个触发器(定时任务类对触发器为多对一)
xml配置例如:
<!-- -->
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="job" />
<property name="startDelay" value="0" /><!-- 调度工厂实例化后,经过0秒开始执行调度 -->
<property name="repeatInterval" value="2000" /><!-- 每2秒调度一次 -->
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job" />
<!-- 每天12:00运行一次 -->
<property name="cronExpression" value="0 0 12 * * ?" />
</bean>
4. xml里配置调度工厂Scheduler,统一调度触发器的地方
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers"><!-- 所有的触发器配到这里,由调度工厂统一调度 -->
<list>
<ref bean="simpleTrigger"/>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
最后,关于使用Quartz的最后一点,我们也可以继承TimerTask,实现定时任务的逻辑,然后通过xml里声明org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean
,指向这个类,其他触发器调度工厂,配置不变的方式,实现定时任务。
使用Spring的Task
Spring引入注解后,很多功能都可以通过注解来配置完成了,当然xml的配置方式也保留了下来,Task也不例外
首先,不管xml方式还是注解方式,都需要在xml配置文件的文件头加入命名空间和描述
xml文件,文件头里新加入命名空间
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
xml方式
1. 实现定时任务类,通过xml配置或者注解,声明这个类
2. 配置任务
<task:scheduled-tasks>
<!-- job为定时任务类,通过xml配置或者注解方式声明了得 -->
<task:scheduled ref="job" method="someMethod" cron="0 15 * * * ?"/>
</task:scheduled-tasks>
注解方式
1. 实现定时任务类,然后对方法加上Scheduled注解,里面还可配置cron表达式,或者固定延迟时间、固定频率
例如:
@Scheduled(cron = "0 0 3 * * ?")
public void task() {
System.out.println(“任务进行中。。。”);
}
2. xml里配置task
<task:executor id="executor" pool-size="5" />
<task:scheduler id="scheduler" pool-size="10" />
<!-- 开启这个配置,spring才能识别@Scheduled注解 -->
<task:annotation-driven executor="executor" scheduler="scheduler" />
参考博客
上述文档里的内容,其实是从三个博客里提出来的,感谢这三位博主的分享
1. Spring定时任务的几种实现
2. 使用Spring的@Scheduled实现定时任务
3. Spring与Quartz的整合实现定时任务调度