springboot整合quartz项目使用(含完整代码)

Posted 小lee学编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot整合quartz项目使用(含完整代码)相关的知识,希望对你有一定的参考价值。

前言:quartz是一个定时调度的框架,就目前市场上来说,其实有比quartz更优秀的一些定时调度框架,不但性能比quartz好,学习成本更低,而且还提供可视化操作定时任务。例如xxl-Job,elastic-Job这两个算是目前工作中使用比较多的定时调度框架了,适配于分布式的项目,性能也是很优秀。这是很多人就很疑惑,既然这样我们为什么还要了解学习quartz呢?我个人觉得学习quartz有两方面,首先xxl-Job,elastic-Job这些框架都是基于quartz的基础上二次开发的,学习quartz更有利于我们加强理解定时调度。第二方面就是工作需求,有一些传统互联网公司还是有很多项目是使用quartz来完成定时任务的开发的,不懂quartz的话,老板叫你写个定时任务都搞不定。

1. quartz的基础概念

有上图可以看到,一个job可以给多个jobDetail封装,一个jobDetail可以给trigger来配置规则,但是一个trigger只能装配一个jobDetail。

scheduler:可以理解为定时任务的工作容器或者说是工作场所,所有定时任务都是放在里面工作,可以开启和停止。

trigger:可以理解为是定时任务任务的工作规则配置,例如说,没个几分钟调用一次,或者说指定每天那个时间点执行。

jobDetail:定时任务的信息,例如配置定时任务的名字,群组之类的。

job:定时任务的真正的业务处理逻辑的地方。

2. quartz的简单使用

这是quartz的api使用,在官网直接提供使用例子,但是在工作中用不到这种方式的

地址:https://www.quartz-scheduler.org/documentation/quartz-2.3.0/quick-start.html

public class QuartzTest 

    public static void main(String[] args) throws Exception
        try 

            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();


            scheduler.start();
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();

            Trigger trigger = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(2)
                            .repeatForever())
                    .build();

            scheduler.scheduleJob(job, trigger);

            TimeUnit.SECONDS.sleep(20);
            scheduler.shutdown();

         catch (SchedulerException se) 
            se.printStackTrace();
        
    

3. quartz与springboot的整合使用

在官网中介绍了,只要你引用了quartz的依赖后,springboot会自适配调度器。当然我们也可以新建bean,修改SchedulerFactoryBean的一些默认属性值。

使用javaBean方式按实际业务需求初始化SchedulerFactoryBean(可以不要,就用默认SchedulerFactoryBean

@Configuration
public class QuartzConfiguration 

    // Quartz配置文件路径
    private static final String QUARTZ_CONFIG = "config/quartz.properties";

    @Value("$task.enabled:true")
    private boolean enabled;

    @Autowired
    private DataSource dataSource;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() 
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setDataSource(dataSource);
        // 设置加载的配置文件
        schedulerFactoryBean.setConfigLocation(new ClassPathResource(QUARTZ_CONFIG));

        // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
        schedulerFactoryBean.setOverwriteExistingJobs(true);

        schedulerFactoryBean.setStartupDelay(5);// 系统启动后,延迟5s后启动定时任务,默认为0

        // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        schedulerFactoryBean.setOverwriteExistingJobs(true);

        // SchedulerFactoryBean在初始化后是否马上启动Scheduler,默认为true。如果设置为false,需要手工启动Scheduler
        schedulerFactoryBean.setAutoStartup(enabled);
        return schedulerFactoryBean;
    

要使用quartz实现定时任务,首先要新建一个Job,在springboot中,新建的Job类要继承QuartzJobBean

public class HelloJob extends QuartzJobBean 
    @Override
    protected void executeInternal(JobExecutionContext context)  
        StringJoiner joiner = new StringJoiner(" | ")
                .add("---HelloJob---")
                .add(context.getTrigger().getKey().getName())
                .add(DateUtil.formatDate(new Date()));
        System.out.println(joiner);

    

创建jobDetail和Trigger来启动定时任务,有两种方式可以实现,本质上就是创建jobDetail和Trigger

方式一:为对应的Job创建JobDetail和Trigger,这种方式有两个注意的地方,jobDetail一定要设置为可持久化.storeDurably(),Trigger创建要用.forJob(“helloJob”),要与JobDetail定义的相同。

@Component
public class HelloJobDetailConfig 

    @Bean
    public JobDetail helloJobDetail()
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("helloJob")
                .storeDurably()
                .usingJobData("data", "保密信息")
                .build();
        return jobDetail;
    

    @Bean
    public Trigger helloJobTrigger()
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob("helloJob")
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(3)
                        .repeatForever())
                .build();

        return trigger;
    

方式二:在注入Bean之前初始化创建JobDetail和Trigger,然后使用Scheduler来调用,跟原生API调用差不多。

@Component
public class HelloJobDetailConfig2 

    @Autowired
    private Scheduler scheduler;

    @PostConstruct
    protected void InitHelloJob() throws Exception 
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("helloJob")
//                .storeDurably()
                .usingJobData("data", "保密信息")
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("helloTrigger")
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(3)
                        .repeatForever())
                .build();
        scheduler.scheduleJob(jobDetail,trigger);

    

4. quartz的持久化

quartz持久化有两种存储,一般情况下quartz相关的表和业务表是放在同一个数据库里的。但是如果考虑性能问题的话,就要配多数据源,业务表单独一个库,quartz相关的表放在一个库。

https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/spring-boot-features.html#boot-features-quartz

spring官网说明,默认情况下,使用内存中的JobStore。但是,如果应用程序中有DataSourcebean,并且spring.quartz是可用的,则可以配置基于JDBC的存储。将相应地配置作业存储类型属性。第二个配置,每次启动先删除表数据再重新创建(在实际生产中,个人更倾向于拿dml来手动创建表,这个值设置为never)。在quartz的jar包里这个路径下有不同数据库的dml:org.quartz.impl.jdbcjobstore

spring.quartz.job-store-type=jdbc

spring.quartz.jdbc.initialize-schema=never

另外一种方式:

要让Quartz使用DataSource而不是应用程序的主DataSource,请声明DataSourcebean,并用@QuartzDataSource注释其@bean方法。这样做可以确保SchedulerFactoryBean和模式初始化都使用Quartz特定的DataSource

@Configuration
public class QuartzDataSourceConfig 

    @Bean
    @QuartzDataSource
    public DataSource quartzDataSource() 
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false");
        return dataSource;
    

还有一点需要注意:当jobbean已经注入spring容器后,下次不用需要再注入,把@Component注释掉。

5. quartz的misfire策略

**misfire:**到了任务触发时间点,但是任务没有被触发

原因:- 使用@DisallowConcurrentExecution注解,而且任务的执行时间>任务间隔

-线程池满了,没有资源执行任务

-机器宕机或者认为停止,果断时间恢复运行。

@DisallowConcurrentExecution:这个是比较常用的注解,证上一个任务执行完后,再去执行下一个任务,不会允许任务并行执行。

@PersistJobDataAfterExecution:任务执行完后,会持久化保留数据到下次 执行

针对不同的ScheduleBuilder,可以设置不同的失火策略,SimpleScheduleBuilder和非SimpleScheduleBuilder,

SimpleScheduleBuilder有六种,而非SimpleScheduleBuilder有三种,在实际工作中我们使用的比较的是CronScheduleBuilder.

.withMisfireHandlingInstructionIgnoreMisfires()
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1
所有未触发的执行都会立即执行,然后触发器再按计划运行。

.withMisfireHandlingInstructionFireAndProceed()
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1
立即执行第一个错误的执行并丢弃其他(即所有错误的执行合并在一起),也就是说无论错过了多少次触发器的执行,都只会立即执行一次,然后触发器再按计划运行。(默认的失火策略)

.withMisfireHandlingInstructionDoNothing()
MISFIRE_INSTRUCTION_DO_NOTHING = 2
所有未触发的执行都将被丢弃,然后再触发器的下一个调度周期按计划运行。

6、总结

关于quartz还有一个很重要的点就是corn表达式,这个个人认为没必要死记硬背,实在不会写的,上网找corn表达式在线转换就可以了。

一个简单demo的代码地址:https://gitee.com/gorylee/quartz-demo
生产项目中quartz的配置使用代码地址:https://gitee.com/gorylee/learnDemo/tree/master/quartzDemo

springboot-01整合quartz

1、什么是quartz?

  quartz是一个开源的定时任务框架,具备将定时任务持久化至数据库以及分布式环境下多节点调度的能力。当当的elastic-job便是以quartz为基础,结合zookeeper开发出来的一款产品。

 

2、整合springboot示例

  项目使用springboot提高开发效率,并将定时任务持久化到mysql数据库中。

2.1)引入quartz依赖

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

2.2)配置quartz

        先引入配置的config代码,在下一节中对每一段配置进行详细解释:
  1 /**
  2  * 分布式定时任务quartz配置
  3  * Created by chenjunyi on 2018/6/6.
  4  */
  5 @Configuration
  6 public class QuartzConfiguration {
  7 
  8     /**
  9      * quartz的JobFactory,根据注册的JobClass从spring应用上下文中获取job实例
 10      */
 11     public static class AutoSpringBeanJobFactory extends AdaptableJobFactory implements SchedulerContextAware {
 12 
 13         /** spring应用上下文 */
 14         private ApplicationContext applicationContext;
 15 
 16         /** scheduler上下文 */
 17         private SchedulerContext   schedulerContext;
 18 
 19         /** 需要忽略的属性 */
 20         private String[]           ignoredUnknownProperties = null;
 21 
 22         private void setApplicationContext(ApplicationContext applicationContext) {
 23             this.applicationContext = applicationContext;
 24         }
 25 
 26         @Override
 27         public void setSchedulerContext(SchedulerContext schedulerContext) {
 28             this.schedulerContext = schedulerContext;
 29         }
 30 
 31         private void setIgnoredUnknownProperties(String... ignoredUnknownProperties) {
 32             this.ignoredUnknownProperties = ignoredUnknownProperties;
 33         }
 34 
 35         @Override
 36         protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
 37             //获取定时任务的clazz,并从spring上下文中获取实例
 38             Class<? extends Job> clazz = bundle.getJobDetail().getJobClass();
 39             Job job = applicationContext.getBean(clazz);
 40 
 41             if (isEligibleForPropertyPopulation(job)) {
 42                 //非继承自QuartzJobBean的Job,设置job属性
 43                 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
 44                 MutablePropertyValues pvs = new MutablePropertyValues();
 45                 if (this.schedulerContext != null) {
 46                     pvs.addPropertyValues(this.schedulerContext);
 47                 }
 48                 pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
 49                 pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
 50                 if (this.ignoredUnknownProperties != null) {
 51                     for (String propName : this.ignoredUnknownProperties) {
 52                         if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
 53                             pvs.removePropertyValue(propName);
 54                         }
 55                     }
 56                     bw.setPropertyValues(pvs);
 57                 } else {
 58                     bw.setPropertyValues(pvs, true);
 59                 }
 60             }
 61             return job;
 62         }
 63 
 64         private boolean isEligibleForPropertyPopulation(Object jobObject) {
 65             return (!(jobObject instanceof QuartzJobBean));
 66         }
 67 
 68     }
 69 
 70     /**
 71      * 配置任务工厂实例
 72      * @param applicationContext spring上下文实例
 73      * @return 任务工厂实例
 74      */
 75     @Bean
 76     public JobFactory jobFactory(ApplicationContext applicationContext) {
 77         AutoSpringBeanJobFactory jobFactory = new AutoSpringBeanJobFactory();
 78         jobFactory.setApplicationContext(applicationContext);
 79         return jobFactory;
 80     }
 81 
 82     /**
 83      * 配置任务调度器,使用项目数据源作为quartz数据源
 84      * @param jobFactory 自定义配置任务工厂
 85      * @param dataSource 数据源实例
 86      * @return 任务调度器
 87      */
 88     @Bean
 89     public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) {
 90         SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
 91         //将spring管理job自定义工厂交由调度器维护
 92         schedulerFactoryBean.setJobFactory(jobFactory);
 93         //设置覆盖已存在的任务(配置已失效,因为改写了原有的注册方式,JOB注册时便已自动进行替换)
 94         schedulerFactoryBean.setOverwriteExistingJobs(true);
 95         //项目启动完成后,等待50秒后开始执行调度器初始化(需要小于JOB的间隔时间)
 96         schedulerFactoryBean.setStartupDelay(50);
 97         //设置调度器自动运行
 98         schedulerFactoryBean.setAutoStartup(true);
 99         //设置数据源,使用与项目统一数据源
100         schedulerFactoryBean.setDataSource(dataSource);
101         //设置定时调度器命名空间
102         schedulerFactoryBean.setSchedulerName("MY-QUARTZ-SCHEDULER");
103         //设置存储在quartz上文中的Spring应用上下文key
104         schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
105         //设置属性
106         Properties properties = new Properties();
107         //设置调度器实例名
108         properties.setProperty("org.quartz.scheduler.instanceName", "SCHEDULER-INSTANCE");
109         //设置调度器实例ID,在cluster中使用,AUTO标识自动生成
110         properties.setProperty("org.quartz.scheduler.instanceId", "AUTO");
111         //禁用rmi配置
112         properties.setProperty("org.quartz.scheduler.rmi.export", "false");
113         //禁用rmi配置
114         properties.setProperty("org.quartz.scheduler.rmi.proxy", "false");
115         //quartz线程池实现类
116         properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
117         //quartz线程池线程数
118         properties.setProperty("org.quartz.threadPool.threadCount", "10");
119         //quartz线程池线程优先级
120         properties.setProperty("org.quartz.threadPool.threadPriority", "5");
121         //quartz线程池是否自动加载数据库内的定时任务
122         properties.setProperty("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
123         //Job错过执行时间的阈值
124         properties.setProperty("org.quartz.jobStore.misfireThreshold", "60000");
125         //Job持久化方式配置
126         properties.setProperty("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
127         //Job的JDBC持久化驱动,此处配置为MySql
128         properties.setProperty("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
129         //配置是否使用
130         properties.setProperty("org.quartz.jobStore.useProperties", "false");
131         //持久化的quartz表结构前缀
132         properties.setProperty("org.quartz.jobStore.tablePrefix", "QRTZ_");
133         //是否是集群quartz
134         properties.setProperty("org.quartz.jobStore.isClustered", "true");
135         //集群quartz中节点有效性检查时间间隔
136         properties.setProperty("org.quartz.jobStore.clusterCheckinInterval", "20000");
137         //错过执行时间的Job最大持有数
138         properties.setProperty("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
139         schedulerFactoryBean.setQuartzProperties(properties);
140         //返回结果
141         return schedulerFactoryBean;
142     }
143 
144 }

 

 2.3)编写定时任务

        为了各个定时任务方便注册,使用模板模式定义一个抽象基类。该抽象基类实现InterruptableJob接口,当然也可以实现其他接口或抽象类(该继承树的顶级接口为org.quartz.Job)。
 1 /**
 2  * 抽象定时任务,完成向quartz注册的功能
 3  * Created by chenjunyi on 2018/6/6.
 4  */
 5 @Slf4j
 6 public abstract class AbstractScheduler implements InterruptableJob {
 7 
 8     @Autowired
 9     private Scheduler scheduler;
10 
11     /**
12      * 向定时任务调度器注册
13      * @throws SchedulerException 注册时发生异常
14      */
15     @PostConstruct
16     protected void register() throws SchedulerException {
17         //任务和触发器名称(若不进行设置,则quartz默认使用UUID,因此每次启动应用都会注册一个新任务)
18         String jobName = this.getClass().getSimpleName() + "Job";
19         String triggerName = this.getClass().getSimpleName() + "Trigger";
20         //设置定时任务
21         JobDetail jobDetail = JobBuilder.newJob(this.getClass()).withIdentity(jobName).build();
22         //创建任务触发器
23         CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(getCron());
24         Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerName).withSchedule(scheduleBuilder).build();
25         //将触发器与任务绑定到调度器内
26         Set<Trigger> triggers = new HashSet<>();
27         triggers.add(trigger);
28         scheduler.scheduleJob(jobDetail, triggers, true);
29         log.info(">>>>>注册[Trigger={}, Job={}]的定时任务成功", triggerName, jobName);
30     }
31 
32     /**
33      * 获取cron表达式
34      * @return cron表达式
35      */
36     protected abstract String getCron();
37 
38 }

 

        实现一个自定义的demo定时任务,并通过@Service交于Spring-IOC容器进行托管,代码如下:

 1 /**
 2  * 示例定时任务
 3  * Created by chenjunyi on 2018/6/5.
 4  */
 5 @Service
 6 @PersistJobDataAfterExecution
 7 @DisallowConcurrentExecution
 8 public class DemoScheduler extends AbstractScheduler {
 9 
10     @Value("${env.cron.demoScheduler}")
11     private String      cron;
12 
13     @Autowired
14     private DemoService demoService;
15 
16     @Override
17     public void interrupt() throws UnableToInterruptJobException {
18 
19     }
20 
21     @Override
22     public void execute(JobExecutionContext context) throws JobExecutionException {
23         demoService.sayHello();
24     }
25 
26     @Override
27     protected String getCron() {
28         return this.cron;
29     }
30 
31 }

3、整合代码详解

3.1)AutoSpringBeanJobFactory

        JobFactory,即定时任务Job工厂,在定时任务触发时,通过该factory获取定时任务实例并执行。该接口的继承树比较简单,实现的子类只有2个,如下图所示:
 
技术分享图片
        不论是AdaptableJobFactory还是SimpleJobFactory,其获取Job实例的方式都比较简单,即通过JobDetail的Class直接newInstance,以AdaptableJobFactory为例,相关源码如下:
 1 @Override
 2 public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
 3     try {
 4         Object jobObject = createJobInstance(bundle); //直接根据class.newInstance获取实例
 5         return adaptJob(jobObject); //job适配,判断job类型并进行包装,此处忽略
 6     } catch (Exception ex) {
 7         throw new SchedulerException("Job instantiation failed", ex);
 8     }
 9 }
10 
11 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
12     return bundle.getJobDetail().getJobClass().newInstance();
13 }
        但这样带来了一个问题,因为我们的定时任务可能会依赖spring-ioc容器中的其他bean(需要进行注入),直接newInstance创建的实例无法被spring-ioc容器托管,从而在执行时拿不到注入的对象,导致NPE(NullPointException)。为了解决这个问题,在网上搜索了解决办法,给出的思路基本都是通过newInstance拿到Job对象后,再手动将其托管到spring-ioc容器中,如下代码所示:
 1 //参考博客链接:https://www.jianshu.com/p/d52d62fb2ac6;作者:恒宇少年
 2 public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
 3 
 4     /** spring-beanfactory */
 5     private transient AutowireCapableBeanFactory beanFactory;
 6 
 7     @Override
 8     public void setApplicationContext(final ApplicationContext context) {
 9         beanFactory = context.getAutowireCapableBeanFactory();
10     }
11 
12     @Override
13     protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
14         final Object job = super.createJobInstance(bundle);
15         beanFactory.autowireBean(job); //通过newInstance获取job实例后,将其交付给spring-ioc
16         return job;
17     }
18 }
  通过在AutowiringSpringBeanJobFactory中维护一个AutowireCapableBeanFactory,并且在获取Job实例之后使用autowireBean方法将其交给spring-ioc进行托管,从而保证该实例在执行过程中能够拿到注入的对象。
        这样的处理同样有个问题,就是每次触发定时任务时,都要newInstance创建对象,那么能不能让Job在spring-ioc容器启动时便被托管(即使用@Service等注解),然后直接获取到这个托管的对象呢。解决这个问题的思路就在于获取Job实例的方式,既然我们可以获取到Job的Class(由Job在向Scheduler注册时设置),那么便可以通过ApplicationContext拿到相应的实例。
        因此,在AutoSpringBeanJobFactory中,取代了AutowireCapableBeanFactory,维护一个ApplicationContext,并通过Job的Class获取实例。
1 Class<? extends Job> clazz = bundle.getJobDetail().getJobClass();
2 Job job = applicationContext.getBean(clazz);

3.2)AbstractScheduler

        该抽象基类实现了自动注册的逻辑,不需要在config中配置额外的注册。思路是通过@PostConstruct注解,在该类被Spring完成初始化后,执行注册动作,有以下几点需要注意:
  • 需要设置任务和触发器名称。如果不设置,quartz默认使用UUID,因此每次启动应用都会注册一个新任务;
  • 调度器的绑定,要使用含有replace功能的方法(方法参数带有boolean replace的)。由于是将Job持久化到数据库,应用再次启动时,会读取数据库中的任务列表。不含replace功能的方法在进行一次新的注册时,发现任务已存在的话,就会报错;而含replace功能的方法会更新数据库的Job配置信息;
  • 由于我们在Job初始化时便进行了任务注册,且采用的是replace的方式,因此在config中的schedulerFactoryBean.setOverwriteExistingJobs(true)该条配置便失效了(因为SchedulerFactoryBean在afterPropertiesSet这个属于SpringBean的生命周期方法中,调用了自身的registerJobsAndTriggers方法,该方法会根据此参数决定是否调用含replace功能的绑定方法进行更新Job,我们自己的Job注册实现中便完成了此功能);
  • 继续上一条,值得注意的是,SchedulerFactoryBean.registerJobsAndTriggers的方法中,会根据是否设置了TransactionManager来决定是否将所有Job和Trigger的更新放在同一个事务中,由于目前的应用没有需要使用事务来更新Job的需求,并且若更新失败,启动应用时会抛出异常,因此该问题放置待解决(解决办法也很简单,在register方法中添加事务控制即可);


以上是关于springboot整合quartz项目使用(含完整代码)的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot开发案例之整合Quartz任务管理系统

SpringBoot 整合Quartz定时任务管理SpringBoot系列18

SpringBoot之旅 -- 定时任务两种(Spring Schedule 与 Quartz 整合 )实现

SpringBoot整合Quartz

springboot整合quartz

quartz - springboot2.1.5 整合