feat: xxz-job 调度中心首次提交
内容: 1. 核心包开发,注解开发 2. 任务扫描以及本地注册 3. 任务执行,任务支持本地单机调用 4. 样本执行器生成
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
53
pom.xml
Normal file
53
pom.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.xiang.pom</groupId>
|
||||
<artifactId>xmc-web-starter</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.xiang</groupId>
|
||||
<artifactId>xservice-quartz</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>xservice-quartz-admin</module>
|
||||
<module>xservice-quartz-core</module>
|
||||
<module>xservice-quartz-common</module>
|
||||
<module>xservice-quartz-sample</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-common</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-mysql-starter</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-cache-starter</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-logger-starter</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
22
xservice-quartz-admin/pom.xml
Normal file
22
xservice-quartz-admin/pom.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>com.xiang</groupId>
|
||||
<artifactId>xservice-quartz</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.xiang.app</groupId>
|
||||
<artifactId>xservice-quartz-admin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.xiang.app;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:09
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class Application {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Application.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
log.info("✅✅✅X-service quartz 任务调度中心启动成功✅✅✅");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.xiang.app.quartz.admin.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2025-12-30 09:42
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("xmc_quartz_config")
|
||||
public class JobConfigDO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* 应用地址
|
||||
*/
|
||||
private String applicationAddress;
|
||||
|
||||
/**
|
||||
* 定时任务名称
|
||||
*/
|
||||
private String beanName;
|
||||
|
||||
/**
|
||||
* 任务执行调度时间
|
||||
*/
|
||||
private String cron;
|
||||
|
||||
/**
|
||||
* 类名
|
||||
*/
|
||||
private String clazz;
|
||||
|
||||
/**
|
||||
* 方法
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 任务开关
|
||||
* 0:关闭 1:开启
|
||||
*/
|
||||
private Integer jobSwitch;
|
||||
|
||||
/**
|
||||
* 删除标识(0:未删除 1:已删除)
|
||||
*/
|
||||
private Integer delFlag;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 上次执行时间
|
||||
*/
|
||||
private LocalDateTime lastRunningTime;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.xiang.app.quartz.admin.domain.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 16:14
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class JobRunningLogDO {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private Long quartzId;
|
||||
|
||||
/**
|
||||
* 执行时间
|
||||
*/
|
||||
private LocalDateTime runningTime;
|
||||
|
||||
/**
|
||||
* 执行状态(0:未执行 1:执行成功 2:执行失败)
|
||||
*/
|
||||
private Integer runningStatus;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.xiang.app.quartz.admin.domain.req;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 15:49
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TaskRegisterRequest {
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* 应用地址
|
||||
*/
|
||||
private String applicationAddress;
|
||||
|
||||
/**
|
||||
* 定时任务名称
|
||||
*/
|
||||
private String beanName;
|
||||
|
||||
/**
|
||||
* 任务执行调度时间
|
||||
*/
|
||||
private String cron;
|
||||
|
||||
/**
|
||||
* 类名
|
||||
*/
|
||||
private String clazz;
|
||||
|
||||
/**
|
||||
* 方法
|
||||
*/
|
||||
private String method;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.xiang.app.quartz.admin.manage;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xiang.app.quartz.admin.domain.entity.JobConfigDO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 15:53
|
||||
*/
|
||||
public interface IQuartzConfigManage extends IService<JobConfigDO> {
|
||||
|
||||
|
||||
List<JobConfigDO> selectByAppName(String name);
|
||||
|
||||
List<JobConfigDO> loadEnabledJobs();
|
||||
|
||||
boolean updateJobLastTriggerTime(String name, LocalDateTime now);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xiang.app.quartz.admin.manage;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xiang.app.quartz.admin.domain.entity.JobConfigDO;
|
||||
import com.xiang.app.quartz.admin.mapper.IQuartzConfigMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 15:53
|
||||
*/
|
||||
@Service
|
||||
public class QuartzConfigManageImpl extends ServiceImpl<IQuartzConfigMapper, JobConfigDO> implements IQuartzConfigManage {
|
||||
@Override
|
||||
public List<JobConfigDO> selectByAppName(String name) {
|
||||
LambdaQueryWrapper<JobConfigDO> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(JobConfigDO::getApplicationName, name);
|
||||
lqw.eq(JobConfigDO::getDelFlag, 0);
|
||||
return baseMapper.selectList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JobConfigDO> loadEnabledJobs() {
|
||||
LambdaQueryWrapper<JobConfigDO> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(JobConfigDO::getJobSwitch, 1);
|
||||
lqw.eq(JobConfigDO::getDelFlag, 0);
|
||||
return baseMapper.selectList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateJobLastTriggerTime(String name, LocalDateTime now) {
|
||||
LambdaUpdateWrapper<JobConfigDO> luw = Wrappers.lambdaUpdate();
|
||||
luw.eq(JobConfigDO::getBeanName, name);
|
||||
luw.set(JobConfigDO::getLastRunningTime, now);
|
||||
return baseMapper.update(luw) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.xiang.app.quartz.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.xiang.app.quartz.admin.domain.entity.JobConfigDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 15:54
|
||||
*/
|
||||
@Repository
|
||||
@Mapper
|
||||
public interface IQuartzConfigMapper extends BaseMapper<JobConfigDO> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.xiang.app.quartz.admin.server;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:18
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class XxzJobRegisterController {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.xiang.app.quartz.admin.service;
|
||||
|
||||
import com.xiang.app.quartz.admin.domain.req.TaskRegisterRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 15:51
|
||||
*/
|
||||
public interface ITaskConfigService {
|
||||
|
||||
/**
|
||||
* 注册单个任务
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
boolean registerTask(TaskRegisterRequest request);
|
||||
|
||||
/**
|
||||
* 批量注册任务
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
boolean registerTasks(List<TaskRegisterRequest> request);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.xiang.app.quartz.admin.service.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.xiang.app.quartz.admin.domain.entity.JobConfigDO;
|
||||
import com.xiang.app.quartz.admin.domain.req.TaskRegisterRequest;
|
||||
import com.xiang.app.quartz.admin.manage.IQuartzConfigManage;
|
||||
import com.xiang.app.quartz.admin.service.ITaskConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 15:52
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TaskConfigServiceImpl implements ITaskConfigService {
|
||||
|
||||
private final IQuartzConfigManage quartzConfigManage;
|
||||
|
||||
@Override
|
||||
public boolean registerTask(TaskRegisterRequest request) {
|
||||
return registerTasks(Collections.singletonList(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerTasks(List<TaskRegisterRequest> request) {
|
||||
|
||||
if (CollectionUtils.isEmpty(request)) {
|
||||
return false;
|
||||
}
|
||||
String applicationName = request.get(0).getApplicationName();
|
||||
List<JobConfigDO> jobs = quartzConfigManage.selectByAppName(applicationName);
|
||||
Map<String, JobConfigDO> existJobs = Maps.newHashMap();
|
||||
if (CollectionUtils.isNotEmpty(jobs)) {
|
||||
existJobs.putAll(jobs.stream().collect(Collectors.toMap(JobConfigDO::getBeanName, Function.identity(), (a, b) -> a)));
|
||||
}
|
||||
List<JobConfigDO> insertList = Lists.newArrayList();
|
||||
List<JobConfigDO> upudateList = Lists.newArrayList();
|
||||
request.forEach(item -> {
|
||||
if (existJobs.containsKey(item.getBeanName())) {
|
||||
JobConfigDO jobConfigDO = existJobs.get(item.getBeanName());
|
||||
putDataIntoDo(item, jobConfigDO, applicationName);
|
||||
upudateList.add(jobConfigDO);
|
||||
} else {
|
||||
JobConfigDO jobConfigDO = new JobConfigDO();
|
||||
putDataIntoDo(item, jobConfigDO, applicationName);
|
||||
insertList.add(jobConfigDO);
|
||||
}
|
||||
});
|
||||
if (CollectionUtils.isNotEmpty(insertList)) {
|
||||
quartzConfigManage.saveBatch(insertList);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(upudateList)) {
|
||||
quartzConfigManage.updateBatchById(upudateList);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void putDataIntoDo(TaskRegisterRequest item, JobConfigDO jobConfigDO, String applicationName) {
|
||||
jobConfigDO.setApplicationName(applicationName);
|
||||
jobConfigDO.setApplicationAddress(item.getApplicationAddress());
|
||||
jobConfigDO.setBeanName(item.getBeanName());
|
||||
jobConfigDO.setCron(item.getCron());
|
||||
jobConfigDO.setClazz(item.getClazz());
|
||||
jobConfigDO.setMethod(item.getMethod());
|
||||
jobConfigDO.setJobSwitch(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xiang.app.quartz.admin.utils;
|
||||
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-05 16:16
|
||||
*/
|
||||
public class CronUtil {
|
||||
|
||||
/**
|
||||
* 判断:
|
||||
* 从 lastTriggerTime 到现在
|
||||
* cron 是否命中过至少一次
|
||||
*/
|
||||
public static boolean isMatch(String cron, LocalDateTime lastTriggerTime) {
|
||||
if (cron == null || cron.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CronExpression expression = CronExpression.parse(cron);
|
||||
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
|
||||
// 第一次执行(从未触发过)
|
||||
if (lastTriggerTime == null) {
|
||||
// 给一个足够早的时间,防止任务永远不触发
|
||||
ZonedDateTime base = now.minusYears(1);
|
||||
ZonedDateTime next = expression.next(base);
|
||||
return next != null && !next.isAfter(now);
|
||||
}
|
||||
|
||||
ZonedDateTime last = lastTriggerTime.atZone(zoneId);
|
||||
ZonedDateTime next = expression.next(last);
|
||||
|
||||
return next != null && !next.isAfter(now);
|
||||
}
|
||||
}
|
||||
34
xservice-quartz-admin/src/main/resources/application-dev.yml
Normal file
34
xservice-quartz-admin/src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
group: DEFAULT_GROUP
|
||||
namespace: 00131110-3ecb-4a35-8bbb-624edde1d937
|
||||
server-addr: general.xiangtech.xyz:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
datasource:
|
||||
dynamic:
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://120.27.153.87:3306/xservice_quartz?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
|
||||
username: quartz
|
||||
password: quartz@123
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
sshConnect: false
|
||||
|
||||
redis:
|
||||
host: r-bp1wt59a6nfyt4e3ltpd.redis.rds.aliyuncs.com
|
||||
port: 6379
|
||||
password: Xiang0000 # 如果无密码可以省略
|
||||
database: 10
|
||||
timeout: 5000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
max-wait: 1000
|
||||
xxz-job:
|
||||
appName: xservice-quartz-admin
|
||||
9
xservice-quartz-admin/src/main/resources/application.yml
Normal file
9
xservice-quartz-admin/src/main/resources/application.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
server:
|
||||
port: 30030
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
application:
|
||||
name: xservice-quartz-admin
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
21
xservice-quartz-common/pom.xml
Normal file
21
xservice-quartz-common/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>com.xiang</groupId>
|
||||
<artifactId>xservice-quartz</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xservice-quartz-common</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
35
xservice-quartz-core/pom.xml
Normal file
35
xservice-quartz-core/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>com.xiang</groupId>
|
||||
<artifactId>xservice-quartz</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xservice-quartz-core</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-common</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-cache-starter</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xiang.core.quartz.annotation;
|
||||
|
||||
import com.xiang.core.quartz.config.XxzJobAutoConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2025-12-30 10:49
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(XxzJobAutoConfiguration.class)
|
||||
public @interface EnableXxzJob {
|
||||
String[] basePackages() default {};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.xiang.core.quartz.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2025-12-30 08:48
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface XxzJob {
|
||||
|
||||
/**
|
||||
* bean的名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* cron调度方法
|
||||
* @return
|
||||
*/
|
||||
String cron();
|
||||
|
||||
boolean enabled() default true;
|
||||
/**
|
||||
* 是否支持多机分布式运行
|
||||
* 若为false:每台机器都会执行一次
|
||||
* 若为true:仅一台机器会执行
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean distributed() default true;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.xiang.core.quartz.boostrap;
|
||||
|
||||
import com.xiang.core.quartz.holder.JobDefinitionHolder;
|
||||
import com.xiang.core.quartz.model.JobDefinition;
|
||||
import com.xiang.core.quartz.schedule.JobScheduler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:50
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JobBootstrap implements ApplicationRunner {
|
||||
|
||||
@Autowired
|
||||
private JobScheduler scheduler;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
JobDefinitionHolder.getAll()
|
||||
.stream()
|
||||
.filter(JobDefinition::isEnabled)
|
||||
.forEach(scheduler::schedule);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.xiang.core.quartz.config;
|
||||
|
||||
import com.xiang.core.quartz.executor.JobExecutor;
|
||||
import com.xiang.core.quartz.model.XxzJobProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:29
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(XxzJobProperties.class)
|
||||
@EnableScheduling
|
||||
public class XxzJobAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public JobExecutor jobExecutor() {
|
||||
return new JobExecutor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.xiang.core.quartz.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 10:20
|
||||
*/
|
||||
@Configuration
|
||||
public class XxzJobTaskScheduleConfig {
|
||||
@Bean
|
||||
public TaskScheduler taskScheduler() {
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(4);
|
||||
scheduler.setThreadNamePrefix("xxz-job-");
|
||||
scheduler.initialize();
|
||||
return scheduler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.xiang.core.quartz.executor;
|
||||
|
||||
import com.xiang.core.quartz.model.JobDefinition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* job任务执行期
|
||||
*
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:42
|
||||
*/
|
||||
@Component
|
||||
public class JobExecutor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(JobExecutor.class);
|
||||
|
||||
/**
|
||||
* v1版本 本地反射调用
|
||||
* @param jobDefinition
|
||||
*/
|
||||
public void executor(JobDefinition jobDefinition) {
|
||||
try {
|
||||
jobDefinition.getMethod().invoke(jobDefinition.getBean());
|
||||
} catch (Exception e) {
|
||||
log.error("xxz-job execute error, job={}", jobDefinition.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xiang.core.quartz.holder;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.xiang.core.quartz.model.JobDefinition;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2025-12-30 08:59
|
||||
*/
|
||||
public class JobDefinitionHolder {
|
||||
|
||||
private static final Map<String, JobDefinition> MAP = Maps.newConcurrentMap();
|
||||
|
||||
private JobDefinitionHolder() {
|
||||
}
|
||||
|
||||
public static void register(JobDefinition job) {
|
||||
if (!MAP.containsKey(job.getName())) {
|
||||
MAP.put(job.getName(), job);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<JobDefinition> getAll() {
|
||||
return MAP.values().stream().toList();
|
||||
}
|
||||
|
||||
public static JobDefinition getOne(String name) {
|
||||
return MAP.get(name);
|
||||
}
|
||||
|
||||
public static void updateOne(JobDefinition jobDefinition) {
|
||||
if (MAP.containsKey(jobDefinition.getName())) {
|
||||
MAP.put(jobDefinition.getName(), jobDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
public static void delOne(JobDefinition jobDefinition) {
|
||||
MAP.remove(jobDefinition.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.xiang.core.quartz.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:30
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class JobDefinition {
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* bean名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* cron
|
||||
*/
|
||||
private String cron;
|
||||
/**
|
||||
* 要执行的方法
|
||||
*/
|
||||
private Method method;
|
||||
/**
|
||||
* spring bean 实例
|
||||
*/
|
||||
private Object bean;
|
||||
/**
|
||||
* 是否支持分布式
|
||||
*/
|
||||
private boolean distributed;
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled;
|
||||
/**
|
||||
* 旧cron
|
||||
*/
|
||||
private String oldCron;
|
||||
/**
|
||||
* 上次执行时间
|
||||
*/
|
||||
private LocalDateTime lastRunningTime;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.xiang.core.quartz.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:30
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "xxz-job")
|
||||
@Data
|
||||
public class XxzJobProperties {
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* DB刷新任务间隔(毫秒)
|
||||
*/
|
||||
private int refreshInterval = 5000;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.xiang.core.quartz.scanner;
|
||||
|
||||
import com.xiang.core.quartz.annotation.XxzJob;
|
||||
import com.xiang.core.quartz.holder.JobDefinitionHolder;
|
||||
import com.xiang.core.quartz.model.JobDefinition;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:40
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JobScanner implements SmartInitializingSingleton {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
|
||||
Map<String, Object> beans =
|
||||
applicationContext.getBeansWithAnnotation(Component.class);
|
||||
|
||||
for (Object bean : beans.values()) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
ReflectionUtils.doWithMethods(
|
||||
targetClass,
|
||||
method -> registerJob(bean, method),
|
||||
method -> AnnotationUtils.findAnnotation(method, XxzJob.class) != null
|
||||
);
|
||||
}
|
||||
}
|
||||
private void registerJob(Object bean, Method method) {
|
||||
XxzJob xxzJob = AnnotationUtils.findAnnotation(method, XxzJob.class);
|
||||
if (Objects.nonNull(xxzJob)) {
|
||||
JobDefinition job = new JobDefinition();
|
||||
job.setName(xxzJob.name());
|
||||
job.setDistributed(xxzJob.distributed());
|
||||
job.setBean(bean);
|
||||
job.setMethod(method);
|
||||
job.setEnabled(xxzJob.enabled());
|
||||
job.setCron(xxzJob.cron());
|
||||
JobDefinitionHolder.register(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.xiang.core.quartz.schedule;
|
||||
|
||||
import com.xiang.core.quartz.executor.JobExecutor;
|
||||
import com.xiang.core.quartz.model.JobDefinition;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:47
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JobScheduler {
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final JobExecutor jobExecutor;
|
||||
|
||||
public void schedule(JobDefinition job) {
|
||||
taskScheduler.schedule(
|
||||
() -> jobExecutor.executor(job),
|
||||
new CronTrigger(job.getCron())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.xiang.core.quartz.config.XxzJobAutoConfiguration
|
||||
34
xservice-quartz-sample/pom.xml
Normal file
34
xservice-quartz-sample/pom.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>com.xiang</groupId>
|
||||
<artifactId>xservice-quartz</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xservice-quartz-sample</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xservice-quartz-core</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xiang.starter</groupId>
|
||||
<artifactId>xmc-mysql-starter</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
21
xservice-quartz-sample/src/main/java/com/xiang/app/Main.java
Normal file
21
xservice-quartz-sample/src/main/java/com/xiang/app/Main.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.xiang.app;
|
||||
|
||||
import com.xiang.core.quartz.annotation.EnableXxzJob;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:52
|
||||
*/
|
||||
@EnableXxzJob(basePackages = {"com.xiang.app.quartz.sample.springboot.schedule.TestXxzJob1"})
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = {
|
||||
"com.xiang.*"
|
||||
})
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.xiang.app.quartz.sample.springboot.schedule;
|
||||
|
||||
import com.xiang.core.quartz.annotation.XxzJob;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author: xiang
|
||||
* @Date: 2026-01-06 09:57
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TestXxzJob1 {
|
||||
|
||||
@XxzJob(name = "TestXxzJob", cron = "0/1 * * * * ? ", enabled = true)
|
||||
public void test() {
|
||||
log.info("任务调度开始");
|
||||
}
|
||||
}
|
||||
39
xservice-quartz-sample/src/main/resources/application.yml
Normal file
39
xservice-quartz-sample/src/main/resources/application.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
xxz-job:
|
||||
app-name: springboot-quartz-test
|
||||
|
||||
spring:
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
application:
|
||||
name: springboot-quartz-test
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
group: DEFAULT_GROUP
|
||||
namespace: 00131110-3ecb-4a35-8bbb-624edde1d937
|
||||
server-addr: general.xiangtech.xyz:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
datasource:
|
||||
dynamic:
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://120.27.153.87:3306/xservice_quartz?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
|
||||
username: quartz
|
||||
password: quartz@123
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
sshConnect: false
|
||||
|
||||
redis:
|
||||
host: r-bp1wt59a6nfyt4e3ltpd.redis.rds.aliyuncs.com
|
||||
port: 6379
|
||||
password: Xiang0000 # 如果无密码可以省略
|
||||
database: 10
|
||||
timeout: 5000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
max-wait: 1000
|
||||
Reference in New Issue
Block a user