spring boot1.4.0+quartz整合

背景:最近手头有一个改造迁移的项目,是spring boot1.4.0,迁移过程中新增了一些自动执行任务,用到了quartz,现整理下供参考。

项目环境:spring boot1.4.0+quartz2.3.0+druid1.0.16。小型集群项目。

引言:quartz2.3.0支持集群,spring对quartz有整合。

正文开始:

1、添加依赖

        <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.3.0</version>
  </dependency>

2、配置quratz

    quartz.properties配置文件:

    # Default Properties file for use by StdSchedulerFactory
    # to create a Quartz Scheduler Instance, if a different
    # properties file is not explicitly specified.
    #
    org.quartz.scheduler.instanceName: MyQuartzScheduler
    #org.quartz.scheduler.rmi.export: false
    #org.quartz.scheduler.rmi.proxy: false
    #org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

    #org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount: 1
    #org.quartz.threadPool.threadPriority: 5
    #org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

    #org.quartz.jobStore.misfireThreshold: 60000

    org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
    org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

    #集群配置

    org.quartz.jobStore.isClustered: true

     org.quartz.jobStore.clusterCheckinInterval: 1000
    org.quartz.jobStore.dataSource = myDS

    #=========================================================================        ===
    # Configure Datasources  

    #=========================================================================    ===

    #自定义DruidPoolingConnectionProvider扩展支持druid

    org.quartz.dataSource.myDS.connectionProvider.class:cn.bj.ykct.hbyc.manager.quartz.DruidPoolingConnecti        onProvider

    org.quartz.dataSource.myDS.driver = org.postgresql.Driver
    org.quartz.dataSource.myDS.URL = jdbc:postgresql://192.168.135.136:5432/wxhepsbc
    org.quartz.dataSource.myDS.user = postgres
    org.quartz.dataSource.myDS.password = postgres
    org.quartz.dataSource.myDS.maxConnection = 5

    项目用的druid连接池,而quartz2.3.0没有对druid实现,但预留了接口,所以我们自己实现。

    DruidPoolingConnectionProvider类

    package cn.bj.ykct.hbyc.manager.quartz;
    import java.sql.Connection;
    import java.sql.SQLException;
    import org.quartz.utils.ConnectionProvider;
    import com.alibaba.druid.pool.DruidDataSource;

    /**
     * 扩展quartz支持druid连接池
     *    
     * @author Danger
     *
     */
    public class DruidPoolingConnectionProvider implements ConnectionProvider {
// JDBC驱动
public String driver;
// JDBC连接串
public String URL;
// 数据库用户名
public String user;
// 数据库用户密码
public String password;
// 数据库最大连接数
public int maxConnection;
// 数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。
public String validationQuery;
private boolean validateOnCheckout;
private int idleConnectionValidationSeconds;
public String maxCachedStatementsPerConnection;
private String discardIdleConnectionsSeconds;
public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
private DruidDataSource datasource;

@Override
public Connection getConnection() throws SQLException {
// TODO Auto-generated method stub
return datasource.getConnection();
}

@Override
public void shutdown() throws SQLException {
// TODO Auto-generated method stub
datasource.close();
}

@Override
public void initialize() throws SQLException {
// TODO Auto-generated method stub
if (URL == null) {
throw new SQLException("DBPool could not be created: DB URL cannot be null");
}
if (driver == null) {
throw new SQLException(
"DBPool '" + URL + "' could not be created: " + "DB driver class name cannot be null!");
}
if (maxConnection < 0) {
throw new SQLException(
"DBPool '" + URL + "' could not be created: " + "Max connections must be greater than zero!");
}
datasource = new DruidDataSource();
datasource.setDriverClassName(driver);
datasource.setUrl(URL);
datasource.setUsername(user);
datasource.setPassword(password);
datasource.setMaxActive(maxConnection);
datasource.setMinIdle(1);
datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);

if (validationQuery != null) {
datasource.setValidationQuery(validationQuery);
if (!this.validateOnCheckout)
datasource.setTestOnReturn(true);
else
datasource.setTestOnBorrow(true);
datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
}
}
--添加 getter setter方法
}

    定义JobFactory,quartz获取到spring的bean.

    package cn.bj.ykct.hbyc.manager.quartz;
    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.scheduling.quartz.AdaptableJobFactory;
    import org.springframework.stereotype.Component;
    /**
     * quartz注入spring bean
     * @author Danger
     *
     */
    @Component
    public class JobFactory extends AdaptableJobFactory{
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}

    }

定义QuartzConfig类

package cn.bj.ykct.hbyc.manager.quartz;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * quartz配置文件
 * 
 * @author Danger
 *
 */
@Configuration
public class QuartzConfig {
@Autowired
DataSource dataSource;
@Autowired
JobFactory jobFactory;

@Bean(name = "jobSchedule")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException, PropertyVetoException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(jobFactory);//上文定义的JobFactory类。
schedulerFactoryBean.setOverwriteExistingJobs(true);//重写已经存在的Job
schedulerFactoryBean.setQuartzProperties(properties());
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
schedulerFactoryBean.setDataSource(dataSource);//设置数据源
return schedulerFactoryBean;

}

        //获取quartz.properties

@Bean
public Properties properties() throws IOException {
// Properties prop = new Properties();
// prop.load(new
// ClassPathResource("/quartz.properties").getInputStream());
// return prop;
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// 在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/*
* quartz初始化监听器
*/
/*@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}*/

}

quartz操作类(添加任务、删除任务、查找所有正在运行的任务、暂停任务、修改任务)

package cn.bj.ykct.hbyc.manager.quartz;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * quartz操作管理类
 * 
 * @author Danger
 *
 */
@Service
public class QuartzManager {
@Autowired
private Scheduler sched;


/**
* 增加一个job

* @param jobClass
*            任务实现类
* @param jobName
*            任务名称
* @param jobGroupName
*            任务组名
* @param jobCron
*            cron表达式(如:0/5 * * * * ? )
*/
public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, String jobCron) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.withSchedule(CronScheduleBuilder.cronSchedule(jobCron)).startNow().build();


sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();
}
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 创建or更新任务,存在则更新不存在创建

* @param jobClass
*            任务类
* @param jobName
*            任务名称
* @param jobGroupName
*            任务组名称
* @param jobCron
*            cron表达式
*/
public void addOrUpdateJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, String jobCron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
addJob(jobClass, jobName, jobGroupName, jobCron);
} else {
if(trigger.getCronExpression().equals(jobCron)){
return;
}
updateJob(jobName, jobGroupName, jobCron);
}
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**

* @param jobClass
* @param jobName
* @param jobGroupName
* @param jobTime
*/
public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, int jobTime) {
addJob(jobClass, jobName, jobGroupName, jobTime, -1);
}

public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, int jobTime, int jobTimes) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
.build();
// 使用simpleTrigger规则
Trigger trigger = null;
if (jobTimes < 0) {
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
.startNow().build();
} else {
trigger = TriggerBuilder
.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
.repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
.startNow().build();
}
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}


public void updateJob(String jobName, String jobGroupName, String jobTime) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
// 重启触发器
sched.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}

/**
* 删除任务一个job

* @param jobName
*            任务名称
* @param jobGroupName
*            任务组名
*/
public void deleteJob(String jobName, String jobGroupName) {
try {
sched.deleteJob(new JobKey(jobName, jobGroupName));
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 暂停一个job

* @param jobName
* @param jobGroupName
*/
public void pauseJob(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}

/**
* 恢复一个job

* @param jobName
* @param jobGroupName
*/
public void resumeJob(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}

/**
* 立即执行一个job

* @param jobName
* @param jobGroupName
*/
public void runAJobNow(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}

/**
* 获取所有计划中的任务列表

* @return
*/
public List<Map<String, Object>> queryAllJob() {
List<Map<String, Object>> jobList = null;
try {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = sched.getJobKeys(matcher);
jobList = new ArrayList<Map<String, Object>>();
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = sched.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Map<String, Object> map = new HashMap<>();
map.put("jobName", jobKey.getName());
map.put("jobGroupName", jobKey.getGroup());
map.put("description", "触发器:" + trigger.getKey());
Trigger.TriggerState triggerState = sched.getTriggerState(trigger.getKey());
map.put("jobStatus", triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
map.put("jobTime", cronExpression);
}
jobList.add(map);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return jobList;
}

/**
* 获取所有正在运行的job

* @return
*/
public List<Map<String, Object>> queryRunJon() {
List<Map<String, Object>> jobList = null;
try {
List<JobExecutionContext> executingJobs = sched.getCurrentlyExecutingJobs();
jobList = new ArrayList<Map<String, Object>>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
Map<String, Object> map = new HashMap<String, Object>();
JobDetail jobDetail = executingJob.getJobDetail();
JobKey jobKey = jobDetail.getKey();
Trigger trigger = executingJob.getTrigger();
map.put("jobName", jobKey.getName());
map.put("jobGroupName", jobKey.getGroup());
map.put("description", "触发器:" + trigger.getKey());
Trigger.TriggerState triggerState = sched.getTriggerState(trigger.getKey());
map.put("jobStatus", triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
map.put("jobTime", cronExpression);
}
jobList.add(map);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return jobList;
}

}

3、编写业务

    package cn.bj.ykct.hbyc.manager.quartz;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    public class TestQuartz extends QuartzJobBean{
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

// TODO Auto-generated method stub

                //业务代码

}
    }

4、测试。

    创建一个job观察运行情况。

    TestQuartz .java

    package cn.bj.ykct.hbyc.manager.quartz;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    public class TestQuartz extends QuartzJobBean{
@Autowired
TestService testService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
testService.service1();
}

}

TestService.java

package cn.bj.ykct.hbyc.manager.quartz;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;

@Service
public class TestService {
private final static Logger LOGGER = LogManager.getLogger(TestService.class);
public void service1(){
LOGGER.info("test service1");
}
}

项目启动后添加任务并启动任务,本例中是implements ApplicationRunner

TestQuartzTrigger.java

package cn.bj.ykct.hbyc.manager.quartz;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class TestQuartzTrigger implements ApplicationRunner {
private final static Logger LOGGER = LogManager.getLogger(TestQuartzTrigger.class);
@Autowired
QuartzManager quartzManager;
@Override
public void run(ApplicationArguments args){
// TODO Auto-generated method stub
LOGGER.info("启动测试testQuartz");

try {

                        //20秒执行一次

quartzManager.addOrUpdateJob(TestQuartz.class, "testQuartz", "testQuartz", "0/20 * * * * ? *");
} catch (Exception e) {
// TODO Auto-generated catch block
LOGGER.error("启动测试testQuartz异常、异常信息:{}",e.getMessage());
e.printStackTrace();
}
}
}

启动项目观察job运行情况:

2018-04-18 10:28:44.705  INFO 5244 --- [           main] o.s.s.q.SchedulerFactoryBean             : Starting Quartz Scheduler now
2018-04-18 10:28:45.158  INFO 5244 --- [           main] o.s.s.q.LocalDataSourceJobStore          : ClusterManager: detected 1 failed or restarted instances.
2018-04-18 10:28:45.158  INFO 5244 --- [           main] o.s.s.q.LocalDataSourceJobStore          : ClusterManager: Scanning for instance "NON_CLUSTERED"'s failed in-progress jobs.
2018-04-18 10:28:45.170  INFO 5244 --- [           main] o.s.s.q.LocalDataSourceJobStore          : ClusterManager: ......Freed 1 acquired trigger(s).
2018-04-18 10:28:45.174  INFO 5244 --- [           main] o.q.c.QuartzScheduler                    : Scheduler jobSchedule_$_NON_CLUSTERED started.
2018-04-18 10:28:45.189  INFO 5244 --- [_MisfireHandler] o.s.s.q.LocalDataSourceJobStore          : Handling 1 trigger(s) that missed their scheduled fire-time.
2018-04-18 10:28:45.251  INFO 5244 --- [           main] b.c.e.u.UndertowEmbeddedServletContainer : Undertow started on port(s) 9012 (http)
2018-04-18 10:28:45.256  INFO 5244 --- [           main] c.b.y.h.m.q.CheckEtcUserJobTrigger       : 项目启动后检查checkEtcUserjob1
2018-04-18 10:28:45.257  INFO 5244 --- [           main] c.b.y.h.m.q.CheckEtcUserJobTrigger       : checkEtcUserjob1 开始启动:2018-04-18 10-28-45
2018-04-18 10:28:45.261  INFO 5244 --- [           main] c.b.y.h.m.q.TestQuartzTrigger            : 启动测试testQuartz
2018-04-18 10:28:45.267  INFO 5244 --- [           main] c.b.y.h.m.ManagerApplication             : Started ManagerApplication in 12.097 seconds (JVM running for 12.989)
2018-04-18 10:28:45.285  INFO 5244 --- [hedule_Worker-1] c.b.y.h.m.q.TestService                  : test service1
2018-04-18 10:29:00.016  INFO 5244 --- [hedule_Worker-1] c.b.y.h.m.q.TestService                  : test service1
2018-04-18 10:29:20.040  INFO 5244 --- [hedule_Worker-1] c.b.y.h.m.q.TestService                  : test service1

2018-04-18 10:29:40.025  INFO 5244 --- [hedule_Worker-1] c.b.y.h.m.q.TestService                  : test service1

正常运行。

总结:

quartz本身支持集群,它是通过数据库中的表来选择触发任务,从而保证不会多个机器执行同一个任务。

spring boot2.0中完美的提供了quartz的支持,你只需引入

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>

    </dependency>

设置是运行在数据库中,application.properties中添加spring.quartz.job-store-type=jdbc

任务类继承QuartzJobBean即可,spring boot会自动查找所有继承QuartzJobBean、实现Job的类。

spring boot2.0整合了quartz,详见下一篇文章。

https://blog.csdn.net/jieyanqulaopo123/article/details/79991781

正文结束。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

myskybeyond

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值