melonjj 2019-11-02
Quartz 是一个很强大的任务调度框架在SpringBoot中也很容易集成
添加依赖:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!--Quartz--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
数据源:我们需要实现一个接口 org.quartz.utils.ConnectionProvider 里面的方法跟 数据库连接池的方法比较相似,Quartz会帮助我们注入各种属性
一般情况下来说可以使用Druid或者C3p0数据源,不过我们项目中之前已经集成过C3p0就不必再创建连接池直接通过反射获取连接池实例即可,如图:connnectionProvider就是我们需要的
贴出主要代码:
package cc.stdpain.xxx.component; import lombok.Data; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.c3p0.internal.C3P0ConnectionProvider; import org.hibernate.internal.NonContextualJdbcConnectionAccess; import org.hibernate.internal.SessionImpl; import org.quartz.utils.ConnectionProvider; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.SQLException; @Data public class QuartzC3p0ConnectionProvider implements ConnectionProvider { //这些属性都会被注入,不过我们项目中已经有现成的连接池 //JDBC驱动 public String driver; //JDBC连接串 public String URL; //数据库用户名 public String user; //数据库用户密码 public String password; //数据库最大连接数 public int maxConnections; //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。 public String validationQuery; private C3P0ConnectionProvider c3P0ConnectionProvider; public QuartzC3p0ConnectionProvider() { try { Field sessionFactorField = MySqlClient.class.getDeclaredField("sessionFactory"); sessionFactorField.setAccessible(true); SessionFactory sessionFactory = (SessionFactory) sessionFactorField.get(MySqlClient.class); Session session = sessionFactory.openSession(); SessionImpl session1 = (SessionImpl) session; NonContextualJdbcConnectionAccess connectionAccess = (NonContextualJdbcConnectionAccess) session1.getJdbcConnectionAccess(); Field providerField = connectionAccess.getClass().getDeclaredField("connectionProvider"); providerField.setAccessible(true); c3P0ConnectionProvider = (C3P0ConnectionProvider) providerField.get(connectionAccess); } catch (NoSuchFieldException e) { e.printStackTrace(); System.exit(-1); } catch (IllegalAccessException e) { e.printStackTrace(); System.exit(-1); } } @Override public Connection getConnection() throws SQLException { return c3P0ConnectionProvider.getConnection(); } @Override public void shutdown() throws SQLException { c3P0ConnectionProvider.stop(); } @Override public void initialize() throws SQLException { //因为我们的连接池已经初始化过了就不用在初始化了 } }
application.properties 文件配置
#Quartz org.quartz.scheduler.instanceName=MyScheduler org.quartz.scheduler.instanceId=NON_CLUSTERED org.quartz.scheduler.skip-update-check=false org.quartz.scheduler.job-factory.class=org.quartz.simpl.SimpleJobFactory ## Quartz 线程池配置 org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=1 #持久化配置 org.quartz.jobStore.misfireThreshold=50000 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.datasource=quartzDataSource org.quartz.jobStore.is-clustered=true org.quartz.jobStore.tablePrefix=QRTZ_ #数据源 #这个Provider就是自己实现的那个类 org.quartz.dataSource.quartzDataSource.connection-provider=cc.stdpain.xxx.component.QuartzC3p0ConnectionProvider #因为我们用的是现成的数据源,这些属性不要也罢不过为了以后方便配置,还是写上比较好 org.quartz.dataSource.quartzDataSource.driver=com.mysql.cj.jdbc.Driver org.quartz.dataSource.quartzDataSource.URL=jdbc:mysql://{ip}:45150/{database}?useSSL=false org.quartz.dataSource.quartzDataSource.user=test_user org.quartz.dataSource.quartzDataSource.password=test_passwd org.quartz.dataSource.quartzDataSource.maxConnections=5 org.quartz.dataSource.quartzDataSource.validationQuery=select 0
Bean配置
@Configuration public class QuartzConfig { @Autowired AppConfig appConfig; //因为数据源依赖MysqlClient中的SessionFactory @DependsOn("mysqlClient") @Bean public Scheduler scheduler() throws IOException, SchedulerException { SchedulerFactory schedulerFactory = new StdSchedulerFactory(quartzProperties()); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); return scheduler; } @Bean public Properties quartzProperties() throws IOException { Properties prop = new Properties(); prop.put("quartz.scheduler.instanceName", appConfig.getQuartzSchedulerInstanceName()); prop.put("org.quartz.scheduler.instanceId", appConfig.getQuartzSchedulerInstanceId()); prop.put("org.quartz.scheduler.skipUpdateCheck", appConfig.getQuartzSchedulerSkipUpdateCheck()); prop.put("org.quartz.scheduler.jobFactory.class", appConfig.getQuartzSchedulerJobFactoryClass()); prop.put("org.quartz.jobStore.class", appConfig.getQuartzJobStoreClass()); prop.put("org.quartz.jobStore.driverDelegateClass", appConfig.getQuartzJobStoreDriverDelegateClass()); prop.put("org.quartz.jobStore.dataSource", appConfig.getQuartzJobStoreDatasource()); prop.put("org.quartz.jobStore.tablePrefix", appConfig.getQuartzJobStoreTablePrefix()); prop.put("org.quartz.jobStore.isClustered", appConfig.getQuartzJobStoreIsClustered()); prop.put("org.quartz.threadPool.class", appConfig.getQuartzThreadPoolClass()); prop.put("org.quartz.threadPool.threadCount", appConfig.getQuartzThreadPoolThreadCount()); prop.put("org.quartz.dataSource.quartzDataSource.connectionProvider.class", appConfig.getQuartzDataSourceQuartzDataSourceConnectionProvider()); prop.put("org.quartz.dataSource.quartzDataSource.driver", appConfig.getQuartzDatasourceQuartzDataSourceDriver()); prop.put("org.quartz.dataSource.quartzDataSource.URL", appConfig.getQuartzDatasourceQuartzDataSourceUrl()); prop.put("org.quartz.dataSource.quartzDataSource.user", appConfig.getQuartzDatasourceQuartzDataSourceUser()); prop.put("org.quartz.dataSource.quartzDataSource.password", appConfig.getQuartzDatasourceQuartzDataSourcePassword()); prop.put("org.quartz.dataSource.quartzDataSource.maxConnections", appConfig.getQuartzDatasourceQuartzDataSourceMaxConnections()); return prop; } }
数据库表结构
-- 如果不支持外键 使用 init2.sql DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(120) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)) ENGINE=InnoDB; CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID)) ENGINE=InnoDB; CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)) ENGINE=InnoDB; CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME)) ENGINE=InnoDB; CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); commit;
我们任务的实体类
@Data public class TaskEntity implements Serializable { private final Long serialVersion = -12654128415L; private Long id; //ID private String jobName; //任务名称 private String jobGroup; //任务分组 private String jobStatus; //任务状态 private String jobClass;//任务执行方法 private String cronExpression; // cron 表达式 private String jobDescription; //任务描述 private String timeZoneId; // 时区 private Long startTime; private Long endTime; private String state; //状态 private String invokeParam;//调用参数 }
添加任务的话 只需要指定 jobName jobGroup jobClass cronExpression jobDescription invokeParam
@Slf4j @Service public class TaskService implements ITaskService { @Autowired private Scheduler scheduler; @Override public void addTask(TaskEntity taskEntity) { String jobName = taskEntity.getJobName(), jobGroup = taskEntity.getJobGroup(), cronExpression = taskEntity.getCronExpression(), jobDescription = taskEntity.getJobDescription(); String invokeParams = taskEntity.getInvokeParam(); try { if (checkExists(jobName, jobGroup)) { //error } TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); JobKey jobKey = JobKey.jobKey(jobName, jobGroup); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing(); CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey). withDescription(jobDescription).withSchedule(scheduleBuilder).build(); trigger.getJobDataMap().put("invokeParam", invokeParams); Class<? extends Job> clazz = (Class<? extends Job>) Class.forName(taskEntity.getJobClass()); JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(jobKey).withDescription(jobDescription).build(); scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException | ClassNotFoundException e) { log.error("", e); } } @Override public void resumeTask(TaskEntity taskEntity) { try { scheduler.resumeJob(JobKey.jobKey(taskEntity.getJobName(), taskEntity.getJobGroup())); } catch (Exception e) { log.error("", e); } } @Override public void updateTask(TaskEntity info) { String jobName = info.getJobName(), jobGroup = info.getJobGroup(), cronExpression = info.getCronExpression(), jobDescription = info.getJobDescription(), createTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"); try { if (!checkExists(jobName, jobGroup)) { //Error } TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); JobKey jobKey = new JobKey(jobName, jobGroup); CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing(); CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withDescription(createTime).withSchedule(cronScheduleBuilder).build(); JobDetail jobDetail = scheduler.getJobDetail(jobKey); jobDetail.getJobBuilder().withDescription(jobDescription); Set<Trigger> triggerSet = new HashSet<>(); triggerSet.add(cronTrigger); scheduler.scheduleJob(jobDetail, triggerSet, true); } catch (SchedulerException e) { log.error("", e); } } @Override public void pauseTask(TaskEntity taskEntity) { TriggerKey triggerKey = TriggerKey.triggerKey(taskEntity.getJobName(), taskEntity.getJobGroup()); try { if (checkExists(taskEntity.getJobName(), taskEntity.getJobGroup())) { scheduler.pauseTrigger(triggerKey); //停止触发器 } } catch (Exception e) { log.error("", e); } } @Override public void deleteTask(TaskEntity taskEntity) { TriggerKey triggerKey = TriggerKey.triggerKey(taskEntity.getJobName(), taskEntity.getJobGroup()); try { if (checkExists(taskEntity.getJobName(), taskEntity.getJobGroup())) { scheduler.pauseTrigger(triggerKey); //停止触发器 scheduler.unscheduleJob(triggerKey); //移除触发器 } } catch (SchedulerException e) { log.error("", e); } } private boolean checkExists(String jobName, String jobGroup) throws SchedulerException { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); return scheduler.checkExists(triggerKey); } }
任务状态查询
SELECT QRTZ_JOB_DETAILS.JOB_NAME AS jobName, QRTZ_JOB_DETAILS.JOB_GROUP AS jobGroup, QRTZ_JOB_DETAILS.JOB_CLASS_NAME AS jobClass, QRTZ_TRIGGERS.DESCRIPTION AS jobDescription, QRTZ_TRIGGERS.TRIGGER_NAME AS triggerName, QRTZ_TRIGGERS.TRIGGER_GROUP AS triggerGroup, QRTZ_CRON_TRIGGERS.CRON_EXPRESSION AS cronExpression, QRTZ_TRIGGERS.START_TIME AS startTime, QRTZ_TRIGGERS.END_TIME AS endTime, QRTZ_TRIGGERS.TRIGGER_STATE AS state, QRTZ_CRON_TRIGGERS.TIME_ZONE_ID AS timeZoneId FROM QRTZ_JOB_DETAILS JOIN QRTZ_TRIGGERS JOIN QRTZ_CRON_TRIGGERS ON QRTZ_JOB_DETAILS.JOB_NAME = QRTZ_TRIGGERS.JOB_NAME AND QRTZ_TRIGGERS.TRIGGER_NAME = QRTZ_CRON_TRIGGERS.TRIGGER_NAME AND QRTZ_TRIGGERS.TRIGGER_GROUP = QRTZ_CRON_TRIGGERS.TRIGGER_GROUP
不过这样创建出来的 Job 声明周期不被Spring管理,因此无法使用依赖注入等,如果需要依赖注入支持还需要一些其他操作
参考:https://yq.aliyun.com/articles/626199