前言
- 今天同事在对接我基于elasticJob二次开发的snailJob时,说需要把每个定时任务都要实现SimpleJob接口,感觉很麻烦,希望直接指定其方法,减少对接的成本
- Quart初始化总结:
2.1 初始化scheduler
2.2 将Job和触发器添加到scheduler中
(1)Job对应于scheduler的jobDetail属性
(2)触发器对应于scheduler的trigger属性 - Quart调用定时任务方法总结
3.1 在JobDetail中指定Quart的Job,Quart调度时会触发Job的execute
3.2 Job#execute触发器JobDataMap中指定类和方法
Spring如何集成Quart
-
Spring集成Quart入口:
<bean name="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="taskInfoSearchTaskJobTrigger" /> <ref bean="trackTrigger" /> </list> </property> <property name="configLocation" value="classpath:quartz.properties"/> </bean>
-
SchedulerFactoryBean实现InitializingBean接口,故在afterPropertiesSet方法时会初始化Scheduler实例
public void afterPropertiesSet() throws Exception { // 初始化scheduler this.scheduler = prepareScheduler(prepareSchedulerFactory()); try { .......... //注册定时任务 registerJobsAndTriggers(); } }
2.1 SchedulerFactoryBean继承SchedulerAccessor,其triggers属性中包含所有的定时任务,在registerJobsAndTriggers方法中将trigger加入到Sheduler
-
将trigger加入到Sheduler(SchedulerAccessor#addTriggerToScheduler)
private boolean addTriggerToScheduler(Trigger trigger) throws SchedulerException { //从trigger中取出jobDetail JobDetail jobDetail = (JobDetail) trigger.getJobDataMap().remove("jobDetail"); if (jobDetail != null && this.jobDetails != null && !this.jobDetails.contains(jobDetail) && (this.overwriteExistingJobs || getScheduler().getJobDetail(jobDetail.getKey()) == null)) { //将trigger加入到Sheduler getScheduler().scheduleJob(jobDetail, trigger); this.jobDetails.add(jobDetail); } }
3.1 首先看一下,配置trigger长啥样?见下方
(1) trigger主要配置定时任务的job以及触发器,那Quart怎样触发Job方法?<bean id="taskInfoSearchTaskJobTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="taskInfoSearchTaskJob" /> <property name="startDelay" value="10000" /><!-- 调度工厂实例化后,经过 秒开始执行调度 --> <property name="repeatInterval" value="1200000" /><!-- 每 秒调度一次 --> </bean>
Quart是如何指定执行方法
-
Job配置如下
1.1 主要配置了Job所在的类以及对应的方法
1.2 MethodInvokingJobDetailFactoryBean继承ArgumentConvertingMethodInvoker(继承MethodInvoker)<bean id="taskInfoSearchTaskJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <ref bean="taskInfoSearchTask" /> </property> <property name="targetMethod"> <value>run</value> </property> </bean>
-
MethodInvokingJobDetailFactoryBean实现FactoryBean,在getObject时返回JobDetail
2.1 在其afterPropertiesSet方法时初始化jobDetailpublic void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException { // 初始化methodInvoker的methodObject属性 prepare(); // Consider the concurrent flag to choose between stateful and stateless job.(默认为MethodInvokingJob) Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class); // Build JobDetail instance. JobDetailImpl jdi = new JobDetailImpl(); jdi.setName(name != null ? name : toString()); jdi.setGroup(this.group); // MethodInvokingJob通过QuartzJobBean实现Quart的Job jdi.setJobClass((Class) jobClass); jdi.setDurability(true); // 重点:最终会在JobRunShell时,将MethodInvokingJobDetailFactoryBean设置到MethodInvokingJob的methodInvoker属性 jdi.getJobDataMap().put("methodInvoker", this); this.jobDetail = jdi; }
3.2 MethodInvoker#prepare分析
public void prepare() throws ClassNotFoundException, NoSuchMethodException { //用户设置的定时任务类 Class<?> targetClass = getTargetClass(); //设置的定时任务方法 String targetMethod = getTargetMethod(); // 用户设置的定时任务方法参数 Object[] arguments = getArguments(); Class<?>[] argTypes = new Class<?>[arguments.length]; for (int i = 0; i < arguments.length; ++i) { argTypes[i] = (arguments[i] != null ? arguments[i].getClass() : Object.class); } try { this.methodObject = targetClass.getMethod(targetMethod, argTypes); } }
-
MethodInvokingJobDetailFactoryBean$MethodInvokingJob#executeInternal执行任务
protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { //this.methodInvoker.invoke()调用指定任务方法 context.setResult(this.methodInvoker.invoke()); }catch (InvocationTargetException ex) { ..... }
3.1 MethodInvoker#invoke分析
public Object invoke() throws InvocationTargetException, IllegalAccessException { // In the static case, target will simply be {@code null}. Object targetObject = getTargetObject(); Method preparedMethod = getPreparedMethod(); if (targetObject == null && !Modifier.isStatic(preparedMethod.getModifiers())) { throw new IllegalArgumentException("Target method must not be non-static without a target"); } ReflectionUtils.makeAccessible(preparedMethod); return preparedMethod.invoke(targetObject, getArguments()); }
SnailJob是如何封装Quart简单介绍
- SnailJob是基于elatiscJob的代码改造出来的公司内部调度框架
- SnailJob创建JobDetail代码示例
private JobDetail createJobDetail() { JobDetail result = JobBuilder.newJob(LiteJob.class).withIdentity(getJobConfig().getJobName()).build(); result.getJobDataMap().put(JOB_EXECUTOR_DATA_MAP_KEY, jobExecutor); return result; }
- Quart调度触发任务的调用栈
优化代码展示
- 在执行任务时,多了一层判断,如果是spring方式的定时任务,则直接反射调用其指定的方法
1.1 直接加if判断,感觉不优雅(后期优化,哈哈哈)
if(snailJob instanceof JobDetailImpl){
JobDataMap jobDataMap = ((JobDetailImpl) snailJob).getJobDataMap();
MethodInvokingJobDetailFactoryBean methodInvoker = (MethodInvokingJobDetailFactoryBean) jobDataMap.get("methodInvoker");
methodInvoker.invoke();
}else{
((SimpleJob)snailJob).execute(shardingContexts.createShardingContext(item));
}