Quartz定时器的使用心得-利用java反射动态调用

第一次写博客,可能不太熟练,望大家体谅,直接上货。我是个小菜鸟,希望能够一起学习,扣扣2541854436。

 

最近学习quartz的使用,采取动态添加定时任务,利用反射来执行具体的任务内容。

项目地址:https://gitee.com/Bdd_ddB/quartz

项目采取了spring boot + maven构建项目。

 

主要使用

  • Scheduler:用于添加定时任务
  • Trigger:用于添加Cron表达式(什么时候触发)
  • JobDetail:设置调用具体类
@Service(value = "jobService")
public class JobService {

    @Autowired
    private ScheduleJobRepository scheduleJobRepository;


    @Autowired
    private Scheduler scheduler; 


    public void addJob(ScheduleJob scheduleJob) throws Exception {
        System.out.println("开始添加任务调度");
        //添加触发调度名称
        TriggerKey triggerKey = getTriggerKey(scheduleJob);
        //设置触发时间
        CronScheduleBuilder cronScheduleBuilder = getCronExpression(scheduleJob);
        //触发建立
        Trigger trigger =         TriggerBuilder.newTrigger().withIdentity(triggerKey).
withSchedule(cronScheduleBuilder).build();

        //添加作业名称
        JobKey jobKey = getJobKey(scheduleJob);
        //建立作业
        JobDetail jobDetail = JobBuilder.newJob(QuartzFactory.class).withIdentity(jobKey).build();
        //传入调度的数据,在QuartzFactory中需要使用
        jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);

        //调度作业
        scheduler.scheduleJob(jobDetail, trigger);

        //把该job添加到数据库中    
        scheduleJobRepository.save(scheduleJob);
    }

 /**
     * 任务key
     *
     * @param scheduleJob
     * @return
     */
    private JobKey getJobKey(ScheduleJob scheduleJob) {
        return JobKey.jobKey(scheduleJob.getId() + scheduleJob.getJobType() + scheduleJob.getJobName());
    }


    /**
     * 获取TriggerKey
     * @param scheduleJob
     * @return
     */
    private TriggerKey getTriggerKey(ScheduleJob scheduleJob) {
        return TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobType());
    }

    /**
     * 获取CronScheduleBuilder,并判断cron是否合法
     * @param scheduleJob
     * @return
     * @throws Exception
     */
    private CronScheduleBuilder getCronExpression(ScheduleJob scheduleJob) throws Exception {
        checkCronExpression(scheduleJob);
        return CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
    }

    /**
     * 检查CronExpression格式是否正确
     * @param scheduleJob
     * @throws Exception
     */
    private void checkCronExpression(ScheduleJob scheduleJob) throws Exception {
        if(!CronExpression.isValidExpression(scheduleJob.getCronExpression()))
            throw new Exception("cron 格式不对");
    }
}
上面设置TiggerKey和JobKey都是自己的业务逻辑来设置的。

本方法中的ScheduleJob是自定义的实体类,里面包含了一些任务信息。
@Data
@Table(name = "schedule_job")
@Entity
@DynamicInsert
public class ScheduleJob{

    /**
     * id
     *
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    /**
     * 任务名称
     */
    @Column(name = "job_name")
    private String jobName;

    /**
     * 任务类型(分组)
     */
    private String jobType;

    /**
     * 任务执行表达式
     *      cron表达式  秒 分 时 天 月 周 年(可选)
     */
    @Column(name = "cron_expression")
    private String cronExpression;

    /**
     * 调用类名称
     */
    @Column(name = "class_name")
    private String className;

    /**
     * 方法参数
     */
    private String params;

    /**
     * 方法名称
     */
    @Column(name = "method_name")
    private String methodName;

    /**
     * 任务状态
     */
    @Column(name = "job_status")
    private String jobStatus;
为什么要有className、methodName、params这三个字段呢?
因为在QuartzFactory类中,我使用了反射,这样就不用创建太多的任务执行类。(暂时只能这样考虑,有任何问题,希望大家跟我交流)
ScheduleJob类应该如何添加数据,请自己设置。提供测试参考


QuartzFactory类是继承了Job类,因为要执行任务的类必须继承Job类。
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class QuartzFactory implements Job {

    @Autowired
    private JobService jobService;


    /**
     * 调用具体的方法
     * 未更改任务调用结束后,消息状态
     *
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println("执行任务");
        //获取调度数据
        ScheduleJob scheduleJob = (ScheduleJob) jobExecutionContext.getMergedJobDataMap().get("scheduleJob");
        //获取对应的Bean
        Object object = SpringUtil.getBean(scheduleJob.getClassName());

        try {
            //获取方法中的参数
            List<ParamsObject> list = ParamsUtil.JSONtoParamsObject(scheduleJob.getParams());
            Class<?> classes[] = (Class<?>[]) getParamsClass(list);
            Object objects[] = getParamsValue(list);
            Method method = object.getClass().getMethod(scheduleJob.getMethodName(), classes);
            method.invoke(object, objects);
            //利用反射执行对应方法
            jobService.deleteJob(scheduleJob);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取方法参数值类型
     */
    private Class[] getParamsClass(List<ParamsObject> list) {
        if(list == null || list.size() == 0)
            return null;
        Class classes[] = new Class[list.size()];
        for (int i = 0; i < classes.length; i++) {
            System.out.println(list.get(i));
            try {
                classes[i] = Class.forName(list.get(i).getName());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return classes;
    }

    
    /**
     * 获取方法参数值
     */
    private Object[] getParamsValue(List<ParamsObject> list) {
        if(list == null || list.size() == 0)
            return null;
        Object objects[] = new Object[list.size()];
        for (int i = 0; i < objects.length; i++) {
            objects[i] = list.get(i).getValue();
        }
        return objects;
    }
}
上述中使用了JSON来传输,最开始使用的Jackson来转换对象,由于Jackson转换List的时候,内部存储对象是LinkedHashMap,会导致类型不匹配的异常。最后采用了阿里巴巴的fastjson来转换json对象。

ParamsUtil由于JSON数据转换

public class ParamsUtil {


    /**
     * 接受List对象
     * @param list
     * @return
     * @throws JsonProcessingException
     */
    public static String paramsListToJSON(List<ParamsObject> list) throws JsonProcessingException {
        String jsonStr = JSON.toJSONString(list);
        return jsonStr;
    }

    /**
     * 接受数组对象
     * 把需要参数类型转成JSON字符串
     * @return
     */
    public static String paramsToJSON(Object...params) throws JsonProcessingException {
        String jsonStr = JSON.toJSONString(params);
        return jsonStr;
    }

    /**
     * 把json字符串转成List<ParamsObject>
     * @param json
     * @return
     * @throws IOException
     */
    public static List<ParamsObject> JSONtoParamsObject(String json) throws IOException {
        List<ParamsObject> list = JSON.parseArray(json,ParamsObject.class);
        return list;
    }
}
​​​​此处用于ParamsObject,由于接受方法参数。
@Data
public class ParamsObject {
    /**
     * 参数类型
     */
    String name;
    /**
     * 参数值
     */
    Object value;

    public ParamsObject(String name, Object value){
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }
}
这样还没结束。。。。。。。。。。。

这样会使在QuartzFactory类中注入失败的。

需要在springboot中这样配置

@Configuration
//开启定时功能
@EnableScheduling
public class TaskConfiguration {

    @Autowired
    private JobFactory jobFactory;

    /**
     *  创建定时任务对象,单例的
     * @return
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(){
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        //将job实例,并能够注入Spring管理的Bean
        schedulerFactoryBean.setJobFactory(jobFactory);
        return schedulerFactoryBean;
    }

    /**
     * 创建scheduler对象
     * @return
     */
    @Bean
    public Scheduler scheduler(){
        return schedulerFactoryBean().getScheduler();
    }
/**
 * 创建job 实例工厂,解决spring注入问题,如果使用默认会导致spring的@Autowired 无法注入问题
 *
 */
@Component
public class JobFactory extends AdaptableJobFactory {
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }

这样就能实现spring注入了。

具体的实现,大家可以去上面地址看看项目,希望大家给我提点意见。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值