SpringBoot整合Quartz定时任务

Quartz是一个定时任务框架,相比较于spring boot 中的@Scheduler可以指定复杂的调度并且可以持久化任务(数据库),其开发难度也比Scheduler难一些.
spring boot @Scheduler创建异步定时任务链接
本文只做springboot与quartz整合的案例,详细了解quartz请看另外一篇博文
quartz使用连接

一 依赖

1 如果是使用的boot与quartz整合的包,只需要添加进去即可

         <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
         </parent>
         <dependencies>
           <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
        </dependencies>

2 如果使用单独的quartz包,还需要添加其他依赖,并需要配置dataSource

      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>
    <!-- 依赖管理 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Freemark support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--c3p0-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!-- mysql driver -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 任务调度 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency> 

这里是基于c3p0,虽然我们开发中都会配置数据源连接池,但是如果单独开发quartz没有配置dataSource的话创建schedulerFactoryBean会报错.

@Configuration
public class DatasourceConfiguration {

    @Bean(name = "dataSource")
    @Qualifier(value = "dataSource")
    @Primary
    @ConfigurationProperties(prefix = "c3p0")
    public DataSource dataSource()1	
    {
        return DataSourceBuilder.create().type(com.mchange.v2.c3p0.ComboPooledDataSource.class).build();
    }
}

二 Quatz的重要组成部分:

API

Scheduler:与被调度的job交互的主要API,负责Job调度执行监控
JobDetail:接收一个job实例并包含job的其他信息
Job:任务执行的逻辑
Trigger:触发器,即任务执行计划,规定了任务的执行策略
了解java多线程的话,可以把jobDetail想象成Thread,job想象成runnable(只是这样想象)

配置文件quartz.poperties(resource目录下)

# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount = 300
# 优先级
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 5000

# 默认存储在内存中
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#持久化
#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.dataSource = qzDS
#org.quartz.dataSource.qzDS.driver = com.mysql.cj.jdbc.Driver
#org.quartz.dataSource.qzDS.URL = jdbc:mysql://10.108.6.123:3306/anjiplus_datacollect?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
#org.quartz.dataSource.qzDS.user = dev
#org.quartz.dataSource.qzDS.password = dev
#org.quartz.dataSource.qzDS.maxConnections = 10
#org.quartz.dataSource.qzDS.connectionProvider.class=com.anjiplus.datacollect.schedule.DruidConnectionProvider

注:这里没有做持久化(如果需要做持久化,到官网中去找建表语句)

三 实际开发

首先添加一个配置类来获取Sheculer实例(直接贴入的项目中即可)

@Configuration
public class SchedulerConfig {

	@Bean(name="SchedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }
	
	@Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        //在quartz.properties中的属性被读取并注入后再初始化对象
        propertiesFactoryBean.afterPropertiesSet();
        Properties properties = propertiesFactoryBean.getObject();
        properties.put("org.quartz.scheduler.instanceName", "DefaultQuartzScheduler");
        return properties;
    }

	/*
     * quartz初始化监听器
     */
    @Bean
    public QuartzInitializerListener executorListener() {
       return new QuartzInitializerListener();
    }
    
    /*
             * 通过SchedulerFactoryBean获取Scheduler的实例
     */
    @Bean(name="Scheduler")
    public Scheduler scheduler() throws IOException, SchedulerException {
       return schedulerFactoryBean().getScheduler();
       //return StdSchedulerFactory.getDefaultScheduler();
    }
   
}

创建一个类用于启动scheduler并初始化job

@Component
public class ScheduleJobManager {
    private static Logger logger = LogManager.getLogger(ScheduleJobManager.class);

    //加入Qulifier注解,通过名称注入bean
    @Autowired
    @Qualifier("Scheduler")
    private Scheduler scheduler;

    @Autowired
    private MonitorJob monitorJob;

    @PostConstruct
    //此注解是实例化这个类时,执行init方法,并且只执行一次
    //把job添加到scheduler中监控执行
    public void init() {
        // 启动调度器
        try {
            if (scheduler != null && !scheduler.isStarted()) {
                scheduler.start();
                logger.info("ScheduleJobManager任务调度服务启动成功!!");

                ///启动monitorjob
                scheduleMonitorJob();

            }
        } catch (Exception e) {
            logger.error("ScheduleJobManager任务调度服务启动出错,err=", e);
        }
    }

    /**
     * 添加job
     */
    private void scheduleMonitorJob() {
        try {
            //构建任务
            String jobName = "monitorJobProxy";
            JobDetail jobDetail = JobBuilder.newJob(MonitorJobProxy.class).withIdentity(jobName).build();
            //传递job,不传递的话可以在MonitorJobProxy中直接写入MonitorJob中的处理过程
            jobDetail.getJobDataMap().put("monitorJob", monitorJob);
            //表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/1 * * * * ?");
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName)
                    .withSchedule(scheduleBuilder).build();

            //SimpleTrigger用来创建简单调度
            // SimpleTrigger trigger = TriggerBuilder.newTrigger()
            //        .withIdentity(jobName)
            //        .startAt(new Date())
            //        .withSchedule(
            //                SimpleScheduleBuilder.simpleSchedule()
            //                        .withIntervalInSeconds(1)
            //                        .withRepeatCount(0)
            //        .build();
            scheduler.scheduleJob(jobDetail, trigger);
            logger.info("MonitorJob成功!");
        } catch (Exception e) {
            logger.error("MonitorJob失败 ,err=" + e);
        }
    }
}

///
@Component
public class MonitorJob   {
    private static Logger logger = LogManager.getLogger(MonitorJob.class);
    @Autowired
    private ScheduleJobManager scheduleManagement;

    public String getJobName() {
        return "主监控job,定时监控&分配任务";
    }

    public void work(JobExecutionContext content) {
        logger.info("任务开始");
        logger.info("任务结束");
    }
}
///
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class MonitorJobProxy implements Job{
	private MonitorJob monitorJob;

	@Override
	public void execute(JobExecutionContext content) throws JobExecutionException {
		JobDataMap jobDataMap = content.getJobDetail().getJobDataMap();
		monitorJob = (MonitorJob)jobDataMap.get("monitorJob");
		monitorJob.work(content);
	}
}

@PersistJobDataAfterExecution
此直接是允许JobDataMap能够更新值(当前job更新,下次job使用新值)
JobDataMap是JobDetail与每次Job实例沟通的介质,每次执行的job实例都可以从jobDatail中的JobDataMap获取里面有的变量值.
@DisallowConcurrentExecution
不允许job并行执行,注意这里说的是job而不是JobDetail,也就是说同一个job多次执行时,前一个job不执行完,后一个job是不允许执行的.

四 Job类中注入Spring Bean问题:

在MonitorJobProxy 中,我们如果想要注入spring boot 容器管理的bean,一种方式是通过其他地方注入并通过JobDataMap传递,第二种方式是new出来,因为在调度时,Job是Scheduler通过反射出来的,所以不受spring boot容器管理,自然就不能自动够注入spring boot的bean.
解决方法有两个:

.第一种是自定义JobFactory,使用AutowireCapableBeanFactory 在Job创建好后,将其中@Autowired的bean注入.

@Compenent
public class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory beanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle)
            throws Exception {
        // TODO Auto-generated method stub
        Object jobInstance = super.createJobInstance(bundle);
        //AutowireCapableBeanFactory将job中的bean注入
        beanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

重写上方SchedulerConfig配置类

    @Autowired
    JobFactory jobFactory;

	@Bean(name="SchedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setJobFactory(jobFactory);
        factory.setStartupDelay(5);
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }
	

上方MonitorJob 添加了@Component所以受spring管理,重写MonitorJobProxy

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class MonitorJobProxy implements Job{
    //直接注入就好,JobFactory在创建MonitorJobProxy实例后把MonitorJob注入进来
	@Autowired
	private MonitorJob monitorJob;

	@Override
	public void execute(JobExecutionContext content) throws JobExecutionException {
		monitorJob.work(content);
	}
}

第二种,我们通过代码中去获取spring容器管理的bean,创建一个Utils,用来获取springboot容器中的bean

@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    } 
}

DataProcessJobProxy :

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class DataProcessJobProxy implements Job{

	
	private DataProcessJob dataProcessJob ;

	@Override
	public void execute(JobExecutionContext content) throws JobExecutionException {

		dataProcessJob = SpringUtils.getBean("DataProcessJob");
		dataProcessJob.work(content);
	}

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot Quartz定时任务是一种在Spring Boot项目中实现定时任务调度的方法。它可以根据设定的时间间隔或者Cron表达式来执行任务。通过引用中提供的源代码和配置文件,可以直接导入Spring Boot项目并配置好数据库即可使用。 为了实现定时任务,需要在项目中创建一个继承自QuartzJobBean的Job类,并重写executeInternal方法。该方法定义了定时任务的具体逻辑,可以在其中执行需要定时执行的业务操作。 另外,还需要创建一个Quartz定时任务的配置类,其中使用@Configuration注解标记为配置类,配置相关的定时任务信息。在该配置类中,可以通过@Bean注解创建JobDetail和Trigger实例,分别定义任务的具体细节和触发器的配置信息。在这个配置类中,我提供了一个示例,用于演示如何配置一个定时任务。 综上所述,Spring Boot Quartz定时任务是一种灵活可靠的定时任务调度方法,可以通过Spring Boot的特性方便地实现任务的定时执行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot+quartz定时任务实现 纯代码](https://download.csdn.net/download/qq_38971617/12284286)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot整合Quartz实现定时任务](https://blog.csdn.net/qq_29305715/article/details/123517569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值