利用学习的时间这里写了个Spring和Quartz结合的一个web项目,纯后端的项目,restful接口
实现对定时任务的增、删、改、查、停止, 启动、定时规则修改、立即执行等。github地址:holly-quartz-web,这里刚开始是为了学习源码,后来有了一些改动,再后来就想做一些业务上的改造,所以clone了一个quartz-core的项目进行改造,后期打算对其集群方式进行改造等等。github地址:quartz-core,有一起感兴趣的朋友可以一起改造,目前的项目比较简单可以作为学习入门的项目,也可以作为搭建job管理系统的初期项目,慢慢迭代。
两个项目 quartz-core是作为一个jar包发布的,holly-quartz-web项目是作为一个war包,启动方式 利用maven tomcat plugin插件的方式部署
quartz-core 项目部署直接mvn install 安装到本地仓库即可。holly-quartz-web pom文件中引用本地的quartz-core
<dependency>
<groupId>org.quartz-scheduler.internal</groupId>
<artifactId>quartz-core</artifactId>
<version>2.3.0-SNAPSHOT</version>
</dependency>
这样debug的时候可以直接进入源码。
holly-quartz-web 启动方式
clean package -Pdev -U -Dmaven.tomcat.uriEncoding=UTF-8 tomcat7:run -Dmaven.test.skip=true -X
想要debug源码的话 source tab标签中必需假如项目源码 如图
整个项目的接口类是JobController类,业务操作类是ScheduleService类,使用的是mysql数据库,这里有建表语句建表及相关sql,对于quartz-core代码的修改后续会尝试提交到组织的。后续列出来。
定时任务对于系统中的应用和作用不言而喻,JAVA实现定时任务的方式比较多,这里文章我们主要看第5种方式。
- 自己在Thread类中利用while循环实现,不过太太简陋,不适合复杂的业务。
- 利用JDK自带的Timer和TimerTask实现,不过还是太简陋,不适合复杂的业务。
- 利用并发包里面的ScheduleExecutorService接口的实现类来搞定。
- Spring3.0以后自带的task xml配置的方式
<beans xmlns="http://www.springframework.org/schema/beans" 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"> <task:scheduled-tasks> <task:scheduled ref="taskJob" method="job1" cron="0 * * * * ?"/>
</task:scheduled-tasks>
```
- Spring和Quartz的整合,也可单独使用Quartz。
关于Quartz这里又有一个pdf写的很不错,贴出地址Quartz Job Scheduling Framework 中文版 V1.0.0.pdf
主要看一张图片对应一下类和图片中的角色就整体把握了框架
图片描述了Scheduler的内部组件结构,SchedulerContext提供Scheduler全局可见的上下文信息,每一个任务都对应一个JobDataMap,虚线表达的JobDataMap表示对应有状态的任务:一个Scheduler可以拥有多个Triger组和多个JobDetail组,注册Trigger和JobDetail时,如果不显式指定所属的组,Scheduler将放入到默认组中,默认组的组名为Scheduler.DEFAULT_GROUP。组名和名称组成了对象的全名,同一类型对象的全名不能相同。
Scheduler本身就是一个容器,它维护着Quartz的各种组件并实施调度的规则。Scheduler还拥有一个线程池,线程池为任务提供执行线程——这比执行任务时简单地创建一个新线程要拥有更高的效率,同时通过共享节约资源的占用。通过线程池组件的支持,对于繁忙度高、压力大的任务调度,Quartz将可以提供良好的伸缩性。
今天先说一下结合的配置以及入口类的。
主要配置在文件app-quartz.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:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="quartzScheduler" name="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="druidDataSource" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="schedulerName" value="QuartzScheduler" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">Taskscheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<!--线程池配置 -->
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">20</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<!--JobStore 配置 -->
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<!-- 集群配置 -->
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">15000</prop>
<prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop>
<prop key="org.quartz.jobStore.misfireThreshold">120000</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
</props>
</property>
</bean>
</beans>
然后再项目中需要用到该bean的地方就直接@Autowired注入就可以了,其中项目中
ScheduleService类中进行了注入
@Autowired
private Scheduler scheduler;// 作业调度器
这里Scheduler 是quartz-core中的一个接口类,但是为什么注入的是org.springframework.scheduling.quartz.SchedulerFactoryBean
呢,debug的时候刚开始我也很不解,首先理一下思路 接口注入的肯定是实现了,但是发现SchedulerFactoryBean并没有实现接口Scheduler,其实这里有一个知识点 起初我是不知道的,后来总算弄明白了。在Spring 在实例化复杂bean的时候 一般采用FactoryBean ,一般的bean 直接用xml配置即可,如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。这里你只要debug一下(后续讲解该bean的实例化)该bean的初始化方法的时候,你就会发现确实比一般的bean负载,涉及到数据源连接,分布式检查启动等。
看一下该类
public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>,
BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {
实现了好多接口 其他的就不说了,只说一下FactoryBean,FactoryBean接口有三个方法的定义
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
其实就是列用反射的方式 实例化真正得到的bean是getObject()得到的,看一下实现方式
private Scheduler scheduler;
@Override
public Scheduler getObject() {
return this.scheduler;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public Class<? extends Scheduler> getObjectType() {
return (this.scheduler != null) ? this.scheduler.getClass() : Scheduler.class;
}
看代码就知道了返回的就是scheduler接口的实现类,其中会根据你的配置方式返回不通的实现类
其实默认返回的是
private Class<? extends SchedulerFactory> schedulerFactoryClass = StdSchedulerFactory.class;
这个工厂类创建的StdScheduler。后面感觉不想写这么细了。主要说一下quartz的几个大模块就可以了,初始化过程。集群实现方式。集群宕机恢复方式。quartz设计的一些取舍,以及主要的一些类,设计模式用到的我了解到了也会说说。后面待续………………