大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
前言
定时任务框架Quartz提供了完备的分布式环境下的定时任务执行能力,本文将基于Quartz框架实现分布式定时任务Starter,以提供在Springboot框架下的开箱即用的分布式定时任务功能。
Github源码地址:honey-scheduler
正文
一. Starter实现
1. 思路概述
因为Quartz框架的核心就是调度器Scheduler,定时任务JobDetail和触发器Trigger,所以分布式定时任务Starter的核心功能就是在应用启动时,自动创建Scheduler,然后加载用户配置的JobDetail和Trigger并注册到Scheduler中。
自动创建Scheduler可以基于SchedulerFactoryBean来实现,这是Spring专门为Quartz提供的用于创建Scheduler的FactoryBean。
加载用户配置的JobDetail和Trigger可以借助ImportBeanDefinitionRegistrar来将用户配置的JobDetail和Trigger全部放到Spring容器中,然后在创建SchedulerFactoryBean时将所有JobDetail和Trigger设置进去。
2. 工程准备
工程目录结构如下所示。
使用到的常量类如下所示。
public class HoneySchedulerConstants {
public static final String HONEY_QUARTZ_PROPERTIES_FILE_PATH = "classpath:honey-quartz.properties";
public static final String HONEY_QUARTZ_PROPERTIES_PREFIX = "org.quartz.";
public static final String HONEY_QUARTZ_PROPERTIES_INSTANCE_NAME = "org.quartz.scheduler.instanceName";
public static final String HONEY_QUARTZ_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String HONEY_DEFAULT_SCHEDULER_INSTANCE_NAME = "honey-default-instance";
public static final String HONEY_JOB_JOB_GROUP = "jobGroup";
public static final String HONEY_JOB_JOB_NAME = "jobName";
public static final String HONEY_JOB_TRIGGER_GROUP = "triggerGroup";
public static final String HONEY_JOB_TRIGGER_NAME = "triggerName";
public static final String HONEY_JOB_START = "start";
public static final String HONEY_JOB_END = "end";
public static final String HONEY_JOB_MISFIRE = "misfire";
public static final String HONEY_SIMPLE_JOB_REPEAT_INTERVAL = "repeatInterval";
public static final String HONEY_SIMPLE_JOB_REPEAT_COUNT = "repeatCount";
public static final String HONEY_CRON_JOB_CRON = "cron";
}
父工程pom文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>honey-starter-scheduler</module>
<module>honey-scheduler-example</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
</parent>
<groupId>com.honey</groupId>
<artifactId>honey-scheduler</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
honey-starter-scheduler工程的pom文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>honey-scheduler</artifactId>
<groupId>com.honey</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>honey-starter-scheduler</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3. 配置加载
我们定义如下注解。
/**
* 定时任务开启注解。<br/>
* 在启动类上添加该注解以启动定时任务。
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({HoneySchedulerPropertiesConfiguration.class})
public @interface EnableHoneyScheduling {
}
上述注解作为整个分布式定时任务模块的开关,如果要开启分布式定时任务,需要在启动类上添加@EnableHoneyScheduling注解。
注意到通过@EnableHoneyScheduling注解导入了HoneySchedulerPropertiesConfiguration,该类用于配置加载,下面看一下其实现。
/**
* 主要作用是将application.yml文件配置的属性与honey-quartz.properties
* 文件配置的属性进行组合。<br/>
* application.yml文件配置优先级大于honey-quartz.properties。
*/
@Configuration
@EnableConfigurationProperties({HoneySchedulerProperties.class})
public class HoneySchedulerPropertiesConfiguration {
@Bean
public Properties schedulerProperties(HoneySchedulerProperties honeySchedulerProperties) {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(
HoneySchedulerPropertiesConfiguration.class.getClassLoader());
// 去classpath下加载honey-quartz.properties配置文件的属性
Properties honeyQuartzProperties = new Properties();
try {
honeyQuartzProperties.load(resourcePatternResolver.getResource(HONEY_QUARTZ_PROPERTIES_FILE_PATH)
.getInputStream());
} catch (Exception e) {
// ignore
}
// 将application.yml文件配置的属性转换成Properties
Properties ymlQuartzProperties = new Properties();
Map<String, String> quartzProperties = honeySchedulerProperties.getQuartz();
if (null != quartzProperties && !quartzProperties.isEmpty()) {
for (Map.Entry<String, String> entry : quartzProperties.entrySet()) {
ymlQuartzProperties.put(HONEY_QUARTZ_PROPERTIES_PREFIX + entry.getKey(), entry.getValue());
}
}
// 组合application.yml配置文件属性与honey-quartz.properties配置文件属性
// 注意application.yml文件配置优先级大于honey-quartz.properties
CollectionUtils.mergePropertiesIntoMap(ymlQuartzProperties, honeyQuartzProperties);
return honeyQuartzProperties;
}
}
用户自定义的配置使用HoneySchedulerProperties来表示,如下所示。
/**
* Quartz定时任务框架的配置属性。<br/>
* 配置的字段与原生Quartz的配置字段保持一致。
*/
@ConfigurationProperties(prefix = "org")
public class HoneySchedulerProperties {
private Map<String, String> quartz = new HashMap<>();
public Map<String, String> getQuartz() {
return quartz;
}
public void setQuartz(Map<String, String> quartz) {
this.quartz = quartz;
}
}
honey-quartz.properties是框架提供的默认兜底配置,如下所示。
org.quartz.scheduler.instanceName=DefaultHoneySchedulerInstance
org.quartz.scheduler.instanceId=AUTO
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.acquireTriggersWithinLock=true
org.quartz.threadPool.threadCount=5
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.scheduler.jmx.export=true
4. Trigger和JobDetail加载
针对Simple执行的任务,定义如下注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Import(HoneyJobRegistrar.class)
public @interface HoneySimpleJob {
/**
* 任务的group。
*/
String jobGroup();
/**
* 任务的name。
*/
String jobName();
/**
* 任务触发器的group。<br/>
* 不配置时取jobGroup的值。
*/
String triggerGroup() default StringUtils.EMPTY;
/**
* 任务触发器的name。<br/>
* 不配置时取jobName的值。
*/
String triggerName() default StringUtils.EMPTY;
/**
* Trigger的重复触发间隔。<br/>
* 单位是毫秒。
*/
String repeatInterval();
/**
* Trigger的重复触发次数。<br/>
* 配置成-1表示一直执行。
*/
String repeatCount();
/**
* 开始时间点。<br/>
* 格式为yyyy-MM-dd HH:mm:ss。<br/>
* 不配置时取当前时间。
*/
String start() default StringUtils.EMPTY;
/**
* 结束时间点。<br/>
* 格式为yyyy-MM-dd HH:mm:ss。
*/
String end() default StringUtils.EMPTY;
/**
* Misfire策略。<br/>
* 策略取值见{@link SimpleTrigger}。
*/
int misfire() default 0;
}
针对Cron执行的任务,定义如下注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Import(HoneyJobRegistrar.class)
public @interface HoneyCronJob {
/**
* 任务的group。
*/
String jobGroup();
/**
* 任务的name。
*/
String jobName();
/**
* 任务触发器的group。<br/>
* 不配置时取jobGroup的值。
*/
String triggerGroup() default StringUtils.EMPTY;
/**
* 任务触发器的name。<br/>
* 不配置时取jobName的值。
*/
String triggerName() default StringUtils.EMPTY;
/**
* 定时任务的Cron表达式。
*/
String cron();
/**
* 开始时间点。<br/>
* 格式为yyyy-MM-dd HH:mm:ss。<br/>
* 不配置时取当前时间。
*/
String start() default StringUtils.EMPTY;
/**
* 结束时间点。<br/>
* 格式为yyyy-MM-dd HH:mm:ss。
*/
String end() default StringUtils.EMPTY;
/**
* Misfire策略。<br/>
* 策略取值见{@link CronTrigger}。
*/
int misfire() default 0;
}
@HoneySimpleJob和@HoneyCronJob注解均复合了@Import注解并导入了HoneyJobRegistrar,并且HoneyJobRegistrar实现了ImportBeanDefinitionRegistrar接口,所以每有一个任务类由@HoneySimpleJob和@HoneyCronJob注解修饰,那么我们就可以在HoneyJobRegistrar中读取被修饰的类的信息然后注册Trigger和JobDetail到Spring容器中,HoneyJobRegistrar实现如下。
/**
* 将由{@link HoneySimpleJob}和{@link HoneyCronJob}注解修饰的任务注册为
* Spring容器的bean。<br/>
* 注解中的配置内容支持配置解析。
*/
public class HoneyJobRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar {
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (importingClassMetadata.hasAnnotation(HoneySimpleJob.class.getName())) {
processSimpleJob(importingClassMetadata, registry);
} else if (importingClassMetadata.hasAnnotation(HoneyCronJob.class.getName())) {
processCronJob(importingClassMetadata, registry);
}
}
private void processSimpleJob(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annotationAttributes = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(HoneySimpleJob.class.getName()));
if (null == annotationAttributes) {
return;
}
// 得到任务的Class对象
Class<?> clazz;
String className = importingClassMetadata.getClassName();
try {
clazz = ClassUtils.forName(className,
Thread.currentThread().getContextClassLoader());
} catch (Exception e) {
throw new IllegalStateException();
}
// 任务类必须是QuartzJobBean类型
if (!QuartzJobBean.class.isAssignableFrom(clazz)) {
throw new IllegalStateException();
}
String jobGroup = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_JOB_GROUP), true);
String jobName = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_JOB_NAME), true);
String triggerGroup = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_TRIGGER_GROUP), true);
String triggerName = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_TRIGGER_NAME), true);
Date startTime = parseDateStr(resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_START), false), true);
Date endTime = parseDateStr(resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_END), false), false);
String interval = resolvePlaceholder(annotationAttributes.getString(HONEY_SIMPLE_JOB_REPEAT_INTERVAL), false);
long repeatInterval = StringUtils.isEmpty(interval) ? 0 : Long.parseLong(interval);
String repeat = resolvePlaceholder(annotationAttributes.getString(HONEY_SIMPLE_JOB_REPEAT_COUNT), false);
int repeatCount = StringUtils.isEmpty(repeat) ? SimpleTrigger.REPEAT_INDEFINITELY : Integer.parseInt(repeat);
int misfire = annotationAttributes.getNumber(HONEY_JOB_MISFIRE);
BeanDefinition jobDetailBeanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(JobDetailFactoryBean.class)
.addPropertyValue("group", jobGroup)
.addPropertyValue("name", jobName)
.addPropertyValue("jobClass", clazz)
.addPropertyValue("durability", true)
.addPropertyValue("requestsRecovery", true)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
registry.registerBeanDefinition(className, jobDetailBeanDefinition);
if (registry instanceof BeanFactory) {
JobDetail jobDetail = ((BeanFactory) registry).getBean(className, JobDetail.class);
BeanDefinition triggerBeanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(SimpleTriggerImpl.class)
.addPropertyValue("group", StringUtils.isEmpty(triggerGroup) ? jobGroup : triggerGroup)
.addPropertyValue("name", StringUtils.isEmpty(triggerName) ? jobName : triggerName)
.addPropertyValue("jobKey", jobDetail.getKey())
.addPropertyValue("startTime", startTime)
.addPropertyValue("endTime", endTime)
.addPropertyValue("repeatInterval", repeatInterval)
.addPropertyValue("repeatCount", repeatCount)
.addPropertyValue("misfireInstruction", misfire)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
registry.registerBeanDefinition(assembleTriggerBeanName(className), triggerBeanDefinition);
}
}
private void processCronJob(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annotationAttributes = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(HoneyCronJob.class.getName()));
if (null == annotationAttributes) {
return;
}
// 得到任务的Class对象
Class<?> clazz;
String className = importingClassMetadata.getClassName();
try {
clazz = ClassUtils.forName(className,
Thread.currentThread().getContextClassLoader());
} catch (Exception e) {
throw new IllegalStateException();
}
// 任务类必须是QuartzJobBean类型
if (!QuartzJobBean.class.isAssignableFrom(clazz)) {
throw new IllegalStateException();
}
String jobGroup = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_JOB_GROUP), true);
String jobName = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_JOB_NAME), true);
String triggerGroup = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_TRIGGER_GROUP), true);
String triggerName = resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_TRIGGER_NAME), true);
String cron = resolvePlaceholder(annotationAttributes.getString(HONEY_CRON_JOB_CRON), true);
Date startTime = parseDateStr(resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_START), false), true);
Date endTime = parseDateStr(resolvePlaceholder(annotationAttributes.getString(HONEY_JOB_END), false), false);
int misfire = annotationAttributes.getNumber(HONEY_JOB_MISFIRE);
BeanDefinition jobDetailBeanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(JobDetailFactoryBean.class)
.addPropertyValue("group", jobGroup)
.addPropertyValue("name", jobName)
.addPropertyValue("jobClass", clazz)
.addPropertyValue("durability", true)
.addPropertyValue("requestsRecovery", true)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
registry.registerBeanDefinition(className, jobDetailBeanDefinition);
if (registry instanceof BeanFactory) {
JobDetail jobDetail = ((BeanFactory) registry).getBean(className, JobDetail.class);
BeanDefinition triggerBeanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(CronTriggerImpl.class)
.addPropertyValue("group", StringUtils.isEmpty(triggerGroup) ? jobGroup : triggerGroup)
.addPropertyValue("name", StringUtils.isEmpty(triggerName) ? jobName : triggerName)
.addPropertyValue("jobKey", jobDetail.getKey())
.addPropertyValue("startTime", startTime)
.addPropertyValue("endTime", endTime)
.addPropertyValue("cronExpression", cron)
.addPropertyValue("misfireInstruction", misfire)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
registry.registerBeanDefinition(assembleTriggerBeanName(className), triggerBeanDefinition);
}
}
private String resolvePlaceholder(String text, boolean notAllowEmpty) {
String resultText = text;
if (StringUtils.isNotEmpty(resultText)) {
resultText = this.environment.resolvePlaceholders(resultText);
}
if (notAllowEmpty && StringUtils.isEmpty(resultText)) {
throw new IllegalStateException();
}
return resultText;
}
private Date parseDateStr(String dateStr, boolean nowWhenEmpty) {
if (StringUtils.isEmpty(dateStr)) {
return nowWhenEmpty ? new Date() : null;
}
SimpleDateFormat sdf = new SimpleDateFormat(HoneySchedulerConstants.HONEY_QUARTZ_DATE_FORMAT);
sdf.setLenient(false);
try {
return new Date(sdf.parse(dateStr).getTime());
} catch (Exception e) {
throw new IllegalStateException();
}
}
private String assembleTriggerBeanName(String jobClassName) {
return jobClassName + ".trigger";
}
}
代码稍微有点长,概括下来就是。
- 得到任务的Class对象;
- 解析@HoneySimpleJob和@HoneyCronJob注解的属性。其中@HoneySimpleJob和@HoneyCronJob注解支持占位符;
- 注册JobDetail;
- 为JobDetail注册对应的Trigger。
5. Scheduler创建
修改@EnableHoneyScheduling注解如下所示。
/**
* 定时任务开启注解。<br/>
* 在启动类上添加该注解以启动定时任务。
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({HoneySchedulerPropertiesConfiguration.class, HoneySchedulerConfiguration.class})
public @interface EnableHoneyScheduling {
}
多引入的HoneySchedulerConfiguration用于创建Scheduler,如下所示。
/**
* 构建{@link Scheduler}调度器。
*/
@Configuration
public class HoneySchedulerConfiguration {
@Bean
@ConditionalOnMissingBean(SchedulerFactoryBean.class)
public SchedulerFactoryBean SchedulerFactoryBean(List<JobDetail> jobDetails, List<Trigger> triggers,
Properties schedulerProperties) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
// 设置实例名
schedulerFactoryBean.setSchedulerName(schedulerProperties.getProperty(
HONEY_QUARTZ_PROPERTIES_INSTANCE_NAME, HONEY_DEFAULT_SCHEDULER_INSTANCE_NAME));
// 设置JobFactory
schedulerFactoryBean.setJobFactory(honeyJobBeanFactory());
// 注册JobDetail
if (null != jobDetails && !jobDetails.isEmpty()) {
schedulerFactoryBean.setJobDetails(jobDetails.toArray(new JobDetail[0]));
}
// 注册Trigger
if (null != triggers && !triggers.isEmpty()) {
schedulerFactoryBean.setTriggers(triggers.toArray(new Trigger[0]));
}
// 默认覆盖已经存在的定时任务
schedulerFactoryBean.setOverwriteExistingJobs(true);
// 将配置设置给调度器
schedulerFactoryBean.setQuartzProperties(schedulerProperties);
return schedulerFactoryBean;
}
@Bean
public HoneyJobBeanFactory honeyJobBeanFactory() {
return new HoneyJobBeanFactory();
}
}
创建Scheduler是基于SchedulerFactoryBean来完成,和使用Quartz原生的api一样,我们需要将所有创建出来的Trigger和JobDetail,以及加载到的配置属性全部设置给Scheduler。注意到上述实现中额外设置了一个JobFactory,这个的作用是什么呢,下面先看一下其实现。
/**
* 将任务对象注册为容器中的bean。<br/>
* {@link SpringBeanJobFactory}能够将{@link JobDetail}注册为容器中的bean,
* 但是依赖注入是构造函数的方式,在{@link HoneyJobBeanFactory}中主要是设置
* 能够支持注解方式的依赖注入。
*/
public class HoneyJobBeanFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
首先说明一下SpringBeanJobFactory,SpringBeanJobFactory会将每一个被执行的JobDetail注册为容器中的bean,此时我们的JobDetail是可以通过构造函数的方式来依赖注入其它bean,然后HoneyJobBeanFactory继承于SpringBeanJobFactory,让JobDetail能够额外的通过注解的方式来依赖注入其它bean。
二. 功能演示
现在来搭建一个示例demo。
首先honey-scheduler-example的pom文件如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>honey-scheduler</artifactId>
<groupId>com.honey</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>honey-scheduler-example</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.honey</groupId>
<artifactId>honey-starter-scheduler</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
创建两个任务类,一个由@HoneySimpleJob注解修饰,一个由@HoneyCronJob注解修饰,如下所示。
@HoneySimpleJob(jobGroup = "testSimpleJobGroup", jobName = "testSimpleJobName" ,
triggerGroup = "testSimpleTriggerGroup", triggerName = "testSimpleTriggerName",
repeatInterval = "${test.simple.job.repeat-interval}",
repeatCount = "${test.simple.job.repeat-count}")
public class TestSimpleJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
System.out.println(LocalDateTime.now().toString() + " Simple-Hello");
}
}
@HoneyCronJob(jobGroup = "testCronJobGroup", jobName = "testCronJobName" ,
triggerGroup = "testCronTriggerGroup", triggerName = "testCronTriggerName",
cron = "*/10 * * * * ?")
public class TestCronJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
System.out.println(LocalDateTime.now().toString() + " Cron-Hello");
}
}
启动类添加@EnableHoneyScheduling注解。
@EnableHoneyScheduling
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
最后是配置文件,如下所示。
org:
quartz:
jobStore:
dataSource: qzDS
dataSource:
qzDS:
driver: com.mysql.cj.jdbc.Driver
URL: jdbc:mysql://localhost:3307/test
user: root
password: root
scheduler:
instanceName: local-demo
test:
simple:
job:
repeat-interval: 10000
repeat-count: -1
运行程序,打印如下。
2024-06-26T15:26:08.005 Simple-Hello
2024-06-26T15:26:10.010 Cron-Hello
2024-06-26T15:26:16.859 Simple-Hello
2024-06-26T15:26:20.007 Cron-Hello
2024-06-26T15:26:26.859 Simple-Hello
2024-06-26T15:26:30.008 Cron-Hello
2024-06-26T15:26:36.857 Simple-Hello
2024-06-26T15:26:40.008 Cron-Hello
定时任务是正常执行的。
总结
Starter的核心实现思路就是将Quartz框架需要的配置,Scheduler,Trigger和JobDetail全部加载出来并完成设置,剩下的就完全由Quartz框架来提供功能。
大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈