feat: xxz-job 调度中心首次提交
内容: 1. 核心包开发,注解开发 2. 任务扫描以及本地注册 3. 任务执行,任务支持本地单机调用 4. 样本执行器生成
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user