第一次写博客,可能不太熟练,望大家体谅,直接上货。我是个小菜鸟,希望能够一起学习,扣扣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注入了。
具体的实现,大家可以去上面地址看看项目,希望大家给我提点意见。