springboot中使用定时任务

2,482 阅读3分钟

一、定时任务spring schedule和quartz

spring 在3.0自己提供了定时活动schdule,配置简单,可以帮我们实现简单的定时任务功能。而quartz是强大的定时任务插件,适合复杂一点的任务。

二、spring shcedule使用方式(使用spirngboot项目作为演示)

1、导入下列依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.3.0</version>
</dependency>

2、在java配置类上添加@EnableSchedule开启对任务计划的支持。

3、然后在spring管理的bean当中是用@Schedule注解来规定任务怎样执行。

@Scheduled(fixedRate = 5000) : fixRate的计时是相对于系统时间的,也就是一定相隔会固定时间执行。

@Scheduled(fixDelay = 2000) :
相对于上一次调用的时间,受其他程序调用的影响。如果该函数在其他地方被手动调用,那么这个计时器就会重新计时。

@Scheduled(corn = "0 28 11 ? * *") : cron表达式

三、使用quartz,使用springboot作为演示。

1、最简单的使用方法

首先,配置一个quartzConfig配置类
@Configuration
public class QuartzConfig {
    // 配置定时任务firstJob
    @Bean
    public MethodInvokingJobDetailFactoryBean jobDetail(FirstJob firstJob) {
        MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();
        // 是否设置并发执行
        jobDetailFactoryBean.setConcurrent(false);
        jobDetailFactoryBean.setTargetObject(firstJob);
        jobDetailFactoryBean.setTargetMethod("print");
        return jobDetailFactoryBean;
    }
    // 配置触发器,这里触发器使用的是cronTrigger,没有使用simpleTrigger,
    @Bean
    public CronTriggerFactoryBean cornTriggerFacrotyBean(JobDetail jobDetail) {
        CronTriggerFactoryBean triggerFactoryBean = new CronTriggerFactoryBean();
        triggerFactoryBean.setCronExpression("1 * * * * ? ");
        triggerFactoryBean.setJobDetail(jobDetail);
        return triggerFactoryBean;
    }
    // 配置调度器
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(Trigger cornTriggerFacrotyBean) {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        bean.setSchedulerName("QUARTZ");
        bean.setTriggers(cornTriggerFacrotyBean);
        bean.setStartupDelay(1);
        return bean;
    }
}
然后,FirstJob类。
@Service
public class FirstJob {
    FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
    public void print () {
        System.out.println("ForstJob ====> print()方法" + fdf.format(new Date()));
    }
}

2、比较复杂的使用,quartz使用灵活。

配置ScheduleConfig,配置类,使用了ConfigurationProperties(prefix = "quartz"),把一些东西放到springboot的配置文件中。注意写任务类的bean名称首字母小写,因为在spring容器中就是存储的小写开头的bean。

quartz.scheduler-name=QUARTZ
quartz.thread-count=10
quartz.thread-name-prefix=quartz_worker
quartz.tasks=\printTask: 1 * * * * ? 

把对应的任务bean和cron表达式,放到配置文件中,方便维护和测试。ScheduleConfig配置java类如下所示。

@Configuration
@ConfigurationProperties(prefix = "quartz")
@Getter
@Setter
public class ScheduleConfig {

    private String schedulerName;

    private String tasks;

    private String threadCount;

    private String threadNamePrefix;

    private final ApplicationContext context;

    @Autowired
    public ScheduleConfig(ApplicationContext context) {
        this.context = context;
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        // 设置调度器名称
        schedulerFactoryBean.setSchedulerName(schedulerName);
        Properties properties = new Properties();
        properties.setProperty("org.quartz.threadPool.threadCount", threadCount);
        properties.setProperty("org.quartz.threadPool.threadNamePrefix", threadNamePrefix);
        schedulerFactoryBean.setQuartzProperties(properties);
        return schedulerFactoryBean;
    }

    @Bean
    public Scheduler scheduler() throws Exception {
        Scheduler scheduler = schedulerFactoryBean().getObject();
        scheduler.scheduleJobs(createJobDetailAndTriggerMap(), true);
        return scheduler;
    }

    private Map<JobDetail, Set<? extends Trigger>> createJobDetailAndTriggerMap() throws NoSuchMethodException, ClassNotFoundException, ParseException {
        Set<String> tasks = StringUtils.commaDelimitedListToSet(this.tasks);
        HashMap<JobDetail, Set<? extends Trigger>> map = new HashMap<>();
        for (String task : tasks) {
            String[] nameAndCron = task.split(":");
            String name = nameAndCron[0];
            String cron = nameAndCron[1];
            // 根据name获取这个jobDetail
            MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();
            jobDetailFactoryBean.setTargetObject(context.getBean(name));
            jobDetailFactoryBean.setTargetMethod("exec");
            jobDetailFactoryBean.setName("quartz");
            jobDetailFactoryBean.afterPropertiesSet();
            // 根据cron配置触发器trigger
            CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
            cronTriggerFactoryBean.setCronExpression(cron);
            cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
            cronTriggerFactoryBean.setName("quartz");
            cronTriggerFactoryBean.afterPropertiesSet();
            CronTrigger cronTrigger = cronTriggerFactoryBean.getObject();
            Set<CronTrigger> triggers = new HashSet<>();
            triggers.add(cronTrigger);
            map.put(jobDetailFactoryBean.getObject(),triggers);
        }
        return map;
    }

}

要注意一定要在jobDetail和触发器cornTrigger设置完要记得afterPropertiesSet();意思就是说把一些其余的配置要自动配置之后,才能使用。不然会报错的。 提供一个jobDetail的案例。因为上面执行的方法都是exec,所以定义一个接口,规范一下所有的任务类。

ITask接口
public interface ITask {
    void exec();
}
PrintTask 任务类
@Component
public class PrintTask implement ITask {

    FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");

    public static Integer count = 0;

    public void exec() {
        System.out.println("quartz很爽\t" + count++ + "\t" + fdf.format(new Date()));
    }
}