定时任务使用

本文介绍了Quartz调度器的基本概念及其与Spring的整合方法,包括核心组件JobDetail和Trigger的作用,以及如何通过Spring配置定时任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、核心概念

 

Quartz的原理不是很复杂,只要搞明白几个概念,然后知道如何去启动和关闭一个调度程序即可。

 

1、Job

表示一个工作,要执行的具体内容。此接口中只有一个方法

void execute(JobExecutionContext context)

 

2、JobDetail

JobDetail表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。

 

3、Trigger代表一个调度参数的配置,什么时候去调。

 

4、Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。

 

 

二、一个最简单入门实例

 

   <!-- 实例化bean -->  
    <bean id="fileBetDiffScheduleService" class="com.cslo.gm.server.service.FileBetDiffScheduleServiceImpl" />
      
    <!-- 配置MethodInvokingJobDetailFactoryBean -->  
    <bean id= "winBetDiffScheduleMethod" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
           <property name="targetObject" ref="fileBetDiffScheduleService"/>  
           <property name="targetMethod" value="fileDownload"/>  
           <property name="concurrent" value="false"/>  
    </bean>  
      
    <!-- 配置定时表达式 -->  
    <bean id= "winBetDiffScheduleTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >  
           <property name="jobDetail" ref="winBetDiffScheduleMethod" />   
          <!-- 每一分钟执行一次 -->   
          <property name="cronExpression" value="0 0 0/1 * * ?" />   
    </bean> 
    <!-- 配置调度工厂 -->  
    <bean id= "testSchedulerFactoryBean"  
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
           <property name="triggers" >  
                 <list>  
                       <!-- <ref bean="winScheduleTrigger" />  
                       <ref bean="winNoScheduleTrigger" />  
                       <ref bean="winBetScheduleTrigger" /> -->
                 </list>  
           </property>  
    </bean>  

 

三、透过实例看原理

 

 

通过研读Quartz的源代码,和本实例,终于悟出了Quartz的工作原理。

 

1、scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。

 

2、JobDetail是一个可执行的工作,它本身可能是有状态的。

 

3、Trigger代表一个调度参数的配置,什么时候去调。

 

4、当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。

 

5、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。

 

6、将上述的结构用一个图来表示,如下:

 

 

"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
 

四、总结

 

1、搞清楚了上Quartz容器执行作业的的原理和过程,以及作业形成的方式,作业注册到容器的方法。就认识明白了Quartz的核心原理。

 

2、Quartz虽然很庞大,但是一切都围绕这个核心转,为了配置强大时间调度策略,可以研究专门的CronTrigger。要想灵活配置作业和容器属性,可以通过Quartz的properties文件或者XML来实现。

 

3、要想调度更多的持久化、结构化作业,可以通过数据库读取作业,然后放到容器中执行。

 

4、所有的一切都围绕这个核心原理转,搞明白这个了,再去研究更高级用法就容易多了。

 

5、Quartz与Spring的整合也非常简单,Spring提供一组Bean来支持:MethodInvokingJobDetailFactoryBean、SimpleTriggerBean、SchedulerFactoryBean,看看里面需要注入什么属性即可明白了。Spring会在Spring容器启动时候,启动Quartz容器。

 

6、Quartz容器的关闭方式也很简单,如果是Spring整合,则有两种方法,一种是关闭Spring容器,一种是获取到SchedulerFactoryBean实例,然后调用一个shutdown就搞定了。如果是Quartz独立使用,则直接调用scheduler.shutdown(true);

 

7、Quartz的JobDetail、Trigger都可以在运行时重新设置,并且在下次调用时候起作用。这就为动态作业的实现提供了依据。你可以将调度时间策略存放到数据库,然后通过数据库数据来设定Trigger,这样就能产生动态的调度。

 

 

2、springboot 中使用的定时器

 

启动类上加注解@EnableScheduling

package com.jinglitong.wallet.job.sendEmail;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.jinglitong.wallet.ddbapi.feign.RegOrderSendEmailApi;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * Copyright (c) 2018, 井立通
 * All rights reserved.
 * 文件名称: RegOrderSendEmailJob.java
 * 作        者: yxl 2018年10月19日
 * 创建时间: 2018年10月19日
 * 功能说明:统计注册用户数、订单数,发送邮件
 */
@Component
@Slf4j
public class RegOrderSendEmailJob {

	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	
	@Value("${countEmail.open}")
	private int releaseOpen = 1;
	
	@Autowired
	private RegOrderSendEmailApi regOrderSendEmailApi;
	
	public static final String countEamil_Job = "countEamil_job";
	
	@Scheduled(cron = "${countEmail.cron}")
	public void send() {
		if(releaseOpen == 1)
			try {
				if (null != redisTemplate.opsForValue().get(countEamil_Job) 
						&& (int) redisTemplate.opsForValue().get(countEamil_Job) == 1) {
					log.error("发送邮件任务再跑着,下次吧!"+redisTemplate.opsForValue().get(countEamil_Job));
				} else {
					log.info("开始发送统计邮件");
					regOrderSendEmailApi.sendEamil();
					redisTemplate.opsForValue().set(countEamil_Job, 1, 20, TimeUnit.HOURS);
				}

			} catch (Exception e) {
				log.error("发送邮件错误:" + e.getMessage());
				e.printStackTrace();
			}finally {
				// 删除标识
				log.info("结束,删除发送邮件标识:"+countEamil_Job);
				redisTemplate.delete(countEamil_Job);

			}
	}
}

 

 

3、SchedulingConfigurer

 

package com.jinglitong.wallet.job.task.baofenExchangeMEI;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import com.jinglitong.wallet.job.service.BaoFenExchangeMEIService;
import com.jinglitong.wallet.job.util.DateUtils;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
@EnableScheduling
public class BaoFenExchangeMEITask implements SchedulingConfigurer {
	/**
	 * 任务开关
	 */
	@Value("${BaoFenExchangeMEI.open}")
	private int baoFenExchangeMEIOpen;

	/**
	 * 任务执行周期
	 */
	@Value("${BaoFenExchangeMEI.cron}")
	private String cron;
	
	@Autowired
    private	BaoFenExchangeMEIService  baoFenExchangeMEIService;
	
	private static int state = 0;

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		// TODO Auto-generated method stub
		if(state == 1) {// 如果一个定时任务 在执行   ,那么 下一个定时  不能执行
			return;
		}

		Runnable task0 = new Runnable() {
			@Override
			public void run() {
				log.info(DateUtils.getDateTime() + "BaoFenExchangeMEITask satrt,宝分上链转账开始...");
				if(baoFenExchangeMEIOpen != 1) {
					return;
				}
				// 定时任务开始执行  等于1
				state = 1;
				 baoFenExchangeMEIService.start();
				log.info(DateUtils.getDateTime() + "BaoFenExchangeMEITask end,宝分上链转账结束...");
				// 定时任务 执行完等于 0
				state = 0;
			}
		};
		Trigger trigger0 = new Trigger() {
			@Override
			public Date nextExecutionTime(TriggerContext triggerContext) {
				// 任务触发.
				CronTrigger trigger = new CronTrigger(cron);
				Date nextExec = trigger.nextExecutionTime(triggerContext);
				return nextExec;
			}
		};
		taskRegistrar.addTriggerTask(task0, trigger0);

	}

}

有多个定时任务的时候,调用任务是使用的一个线程,需要并行执行,需要配置线程池

线程池配置:

package com.jinglitong.wallet.job.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class ThreadManager {
    @Bean
    public TaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        return taskScheduler;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值