springboot定时服务

上一篇文章【修改定时时间,定时任务及时生效】  是定时任务与功能项目共用一个;

我目前所在公司的定时服务是专门有一个项目处理,然后定时查询库里面的定时信息配置。

使用Quartz定时任务框架

话不多说,上程序

数据库设置

create table SCHEDULER_JOB
(
  id       VARCHAR2(32) not null,
  job_name VARCHAR2(200),
  cron     VARCHAR2(100),
  method_name varchar2(200),
  bean_name varchar2(200),
  remark varchar2(50),
  type varchar2(4) default '是否有参数 01否 02是'
)

实体类 SchedulerJob

package com.example.demo.entity;

public class SchedulerJob {

    private String id;
    //任务名称
    private String jobName;
    //表达式
    private String cron;
    //方法名称
    private String methodName;
    //bean名称
    private String beanName;
    //备注(方法的参数)
    private String remark;
    //类型 是否有参数 01否 02是
    private String type;

    //todo get/set方法
}

SchedulerJobMapper

import com.example.demo.entity.SchedulerJob;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface SchedulerJobMapper {

    List<SchedulerJob> queryList();
}

SchedulerJobMapper.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.SchedulerJobMapper">

    <resultMap type="com.example.demo.entity.SchedulerJob" id="resultMap">
        <result property="id" 			column="ID"/>
        <result property="jobName" 		column="JOB_NAME"/>
        <result property="cron"		column="CRON"/>
        <result property="methodName"		column="METHOD_NAME"/>
        <result property="beanName"		column="BEAN_NAME"/>
        <result property="remark"		column="REMARK"/>
        <result property="type"		column="TYPE"/>
    </resultMap>

    <select id="queryList" resultMap="resultMap">
        select ID,JOB_NAME,CRON,METHOD_NAME,BEAN_NAME,REMARK,TYPE from  scheduler_job
    </select>

</mapper>

JobRegisterService 用分布式锁定时查询定时任务

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

@Service
public class JobRegisterService {

    private static final Logger logger = LoggerFactory.getLogger(JobRegisterService.class);

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private JobService jobService;

    @Scheduled(cron = "0 0/2 * * * ?")
    public void startTaskJob() {
        Lock lock = redisLockRegistry.obtain("lock");
        try {
            boolean flag = lock.tryLock(10L, TimeUnit.SECONDS);
            ValueOperations<String, Object> map = redisTemplate.opsForValue();
            if (flag) {
                Object taskStatus = map.get("taskStatus");
                if (taskStatus != null && taskStatus.toString().equals("Y")) {
                    jobService.clearAllJob();
                    return;
                }
                map.set("taskStatus", "Y");
                redisTemplate.expire("taskStatus", 1L, TimeUnit.MINUTES);
                jobService.timeTask();
            } else {
                jobService.clearAllJob();
            }
        } catch (Exception e) {
            logger.error("获取锁异常:", e);
        } finally {
            lock.unlock();
        }
    }
}

JobService  查询数据库,并为每个job设置对应的表达式cron

import com.alibaba.fastjson.JSON;
import com.example.demo.entity.SchedulerJob;
import com.example.demo.mapper.SchedulerJobMapper;
import com.example.demo.util.DateUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class JobService {

    private static final Logger logger = LoggerFactory.getLogger(JobService.class);

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private SchedulerJobMapper schedulerJobMapper;

    public void timeTask() {
        //查询数据库存在需要定时的任务
        logger.info("任务重置开始,查询数据...");
        List<SchedulerJob> schedulerJobList = schedulerJobMapper.queryList();
        if (schedulerJobList != null) {
            try {
                scheduler.clear();
                logger.info("任务重置开始,旧的任务清理");
                schedulerJobList.forEach(this::addJob);
            } catch (Exception e) {
                logger.error("调用错误:", e);
            }
        }
    }

    public void addJob(SchedulerJob schedulerJob) {
        try {
            //作业名称
            JobKey jobKey = JobKey.jobKey(schedulerJob.getJobName());
            //设置触发时间
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(schedulerJob.getCron());
            //触发建立
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(schedulerJob.getJobName()).withSchedule(cronScheduleBuilder).forJob(jobKey).build();
            if (trigger != null && trigger.getStartTime() != null) {
                logger.info("作业【{}】启动时间为{}", schedulerJob.getJobName(), DateUtils.formatDate(trigger.getStartTime(), DateUtils.DATETIME_FORMAT));
            } else {
                logger.info("作业【{}】启动时间为空", schedulerJob.getJobName());
            }
            //建立作业
            JobDetail jobDetail = JobBuilder.newJob(QuartzFactory.class).withIdentity(schedulerJob.getJobName()).build();
            jobDetail.getJobDataMap().put("schedulerJob", schedulerJob);
            //调度作业
            scheduler.scheduleJob(jobDetail, trigger);

            if (!scheduler.isShutdown()) {
                scheduler.start();
            }
        } catch (Exception e) {
            logger.error("添加作业失败[schedulerJob={}]", JSON.toJSONString(schedulerJob), e);
        }
    }

    /**
     * 清除任务
     */
    public void clearAllJob() throws SchedulerException {
        logger.info("清除任务...");
        scheduler.clear();
    }
}

QuartzFactory 定时任务工厂 根据method_name找到对应的映射信息,并执行该方法

import com.example.demo.constant.SchedulerJobTypeEnum;
import com.example.demo.entity.SchedulerJob;
import com.example.demo.util.SpringContextUtil;
import org.apache.commons.lang.StringUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class QuartzFactory implements Job {

    private static final Logger logger = LoggerFactory.getLogger(QuartzFactory.class);

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //获取调度数据
        SchedulerJob schedulerJob = (SchedulerJob) jobExecutionContext.getMergedJobDataMap().get("schedulerJob");
        //获取对应的bean
        Object obj = SpringContextUtil.getObj(schedulerJob.getBeanName());
        try {
            if (obj == null) {
                throw new Exception("找不到该类");
            }
            if (StringUtils.isEmpty(schedulerJob.getType()) || SchedulerJobTypeEnum.NO.getCode().equals(schedulerJob.getType())) {
                Method method = obj.getClass().getMethod(schedulerJob.getMethodName());
                method.invoke(obj);
            } else {
                Method method = obj.getClass().getMethod(schedulerJob.getMethodName(), String.class);
                method.invoke(obj, schedulerJob.getRemark());
            }
        } catch (Exception e) {
            logger.error("定时任务获取映射异常:", e);
        }
    }
}

SpringContextUtil 上下文工具

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware {

    //spring 上下文实例对象
    private static ApplicationContext context;

    // 根据class获取对象实例
    public static <T> T getObj(Class<T> tClass) {
        return context.getBean(tClass);
    }

    // 根据配置的 bean name 获取对象实例
    public static Object getObj(String beanName) {
        return context.getBean(beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}

RedisLockConfig Redis分布式锁配置bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.integration.redis.util.RedisLockRegistry;

@Configuration
public class RedisLockConfig {

    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        //第一个参数redisConnectionFactory
        //第二个参数registryKey,分布式锁前缀,设置为项目名称会好些
        //该构造方法对应的分布式锁,默认有效期是60秒.可以自定义
        return new RedisLockRegistry(redisConnectionFactory, "demo");
    }
}

SchedulerConfig 定时任务配置bean

import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.io.IOException;

@Configuration
public class SchedulerConfig {

    @Bean
    public Scheduler scheduler() throws Exception {
        Scheduler scheduler = schedulerFactoryBean().getScheduler();
        return scheduler;
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setSchedulerName("Test_Scheduler");
        //覆盖已存在的任务
        factory.setOverwriteExistingJobs(true);
        // 延迟10s执行 防止发生系统未启动完成 定时任务却已经开始的情况
        factory.setStartupDelay(10);
        return factory;
    }
}

SchedulerJobTypeEnum 定时任务类型枚举

public enum SchedulerJobTypeEnum {

    NO("01", "否"),
    YES("02", "是");

    private String code;
    private String value;

    SchedulerJobTypeEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

DateUtils 时间处理工具类

import org.apache.commons.lang.time.DateFormatUtils;

import java.util.Date;

public class DateUtils {

    public static final String DATE_FORMAT = "yyyy-MM-dd";

    public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final String TIME_FORMAT = "HH:mm:ss";

    /**
     * 常用的时间格式.
     */
    private static String[] parsePatterns = {"yyyy-MM-dd",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm",
            "HH:mm:ss"};

    /**
     * 得到当前日期字符串.
     */
    public static String getDate() {
        return getDate(DateUtils.DATE_FORMAT);
    }

    public static String getDate(String pattern) {
        return DateFormatUtils.format(new Date(), pattern);
    }

    /**
     * 得到当前时间字符串.
     */
    public static String getTime() {
        return formatDate(new Date(), DateUtils.TIME_FORMAT);
    }

    /**
     * 得到当前日期和时间字符串.
     */
    public static String getDateTime() {
        return formatDate(new Date(), DateUtils.DATETIME_FORMAT);
    }

    /**
     * 获取日期时间字符串,默认格式为(yyyy-MM-dd).
     */
    public static String formatDate(Date date, Object... pattern) {
        String formatDate = null;
        if (pattern != null && pattern.length > 0) {
            formatDate = DateFormatUtils.format(date, pattern[0].toString());
        } else {
            formatDate = DateFormatUtils.format(date, DateUtils.DATE_FORMAT);
        }
        return formatDate;
    }
}

TestSchedulerJobService 定义的定时任务方法

import com.example.demo.util.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class TestSchedulerJobService {

    private static final Logger logger = LoggerFactory.getLogger(TestSchedulerJobService.class);

    public void test() {
        String dateTime = DateUtils.getDateTime();
        logger.info("当前时间={}", dateTime);
    }

    public void test1(String param) {
        String dateTime = DateUtils.getDateTime();
        logger.info("当前时间1={},参数={}", dateTime, param);
    }
}

数据库补充数据

【注意:】数据库中bean_Name存储的是bean名称(默认类名首字母小写)

job_name和method_name数据值一样即可

94071be2e0b8490384d6a535dbffd8cb.jpeg

 测试结果

76302de2f0fa435baaf5f7b3d50ccc5a.jpeg

 由此可看出,两个定时任务是按照数据库中配置的定时时间跑的。

具体定时任务时间等信息视情况而定!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值