前两天看了一下项目中正在用的worker,是在spring中利用spring.xml配置的,所以今天想把worker搬到springboot上来,springboot提倡全注解形式,所以本demo不含任何xml配置。下面我想把我的demo的文件展示出来,并一个个解释其中遇到的问题以及疑问。
先看一下我的目录结构。
我们进入我们的job文件。其在我们的task包下,现在我们提倡面向接口编程,这样会提高我们程序的拓展性。下面是这两个java文件。
ScheduleTask.java
package com.example.scheduledemo.task;
public interface ScheduleTask {
void sayHello();
}
ScheduleTaskImpl.java
package com.example.scheduledemo.task.Impl;
import com.example.scheduledemo.task.ScheduleTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class ScheduleTaskImpl implements ScheduleTask {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleTaskImpl.class);
@Override
public void sayHello() {
LOGGER.info("Hello world, i'm the king of the world!!!");
}
}
下面是我们的配置类。
SpringBeans.java
package com.example.scheduledemo.config;
import com.example.scheduledemo.task.ScheduleTask;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
public class SpringBeans {
@Bean(name="jobDetail")
public MethodInvokingJobDetailFactoryBean jobDetail(ScheduleTask scheduleTask){
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
jobDetail.setConcurrent(false); //是否允许并发,假设当前job执行时间为10s, 单任务的时间间隔为5s, 则存在并发情况,为false则等待上一个任务执行完,否则并发执行
jobDetail.setName("xia-test");
jobDetail.setGroup("xia");
jobDetail.setTargetObject(scheduleTask); //jobBean
jobDetail.setTargetMethod("sayHello"); //scheduleTask中的方法名 任务调度器实际执行的就是这个方法
return jobDetail;
}
@Bean(name="jobTrigger")
public CronTriggerFactoryBean jobTrigger(JobDetail jobDetail){
CronTriggerFactoryBean jobTrigger = new CronTriggerFactoryBean();
jobTrigger.setJobDetail(jobDetail);
jobTrigger.setCronExpression("0/2 * * * * ?");
jobTrigger.setName("xia-test");
return jobTrigger;
}
@Bean(name="scheduler")
public SchedulerFactoryBean scheduler(Trigger cronJobTrigger){
SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
scheduler.setOverwriteExistingJobs(true);
scheduler.setStartupDelay(1);
scheduler.setTriggers(cronJobTrigger);
return scheduler;
}
}
下面我想着重强调一下这个配置文件,这个是基于注解的。跟xml一点也一样。首先我们定义了一个job,即任务调度器实际上要干的工作,然后定义一个触发器,即什么时候,以什么样的方式调用执行这个任务。然后把这个触发器放到我们的schedule即任务调度器中,注意,这个任务调度器是spirng容器帮我们管理的,我们只需要运行spring容器,即这个springboot项目就可以了,因为我们通过触发器已经设定了这个任务该怎样执行。所以接下来的是只是让spring去执行他。
这个配置文件看起来很简单,单如果你真觉得这样那就错了,举个栗子,细心的同学可能发现为什么我定义了一个CronTriggerFactoryBean类型的bean 却用Tigger去接收,如果你回答因为CronTriggerFactoryBean是Tigger的子类,那么恭喜你蒙对了一般,你可以看一下CronTriggerFactoryBean的实现,
public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNameAware, InitializingBean
好了,接下来我们解释为什么会这样。
NB: A bean that implements this interface cannot be used as a normal bean. A FactoryBean is defined in a bean style, but the object exposed for bean references (getObject()
) is always the object that it creates.
这是官网上的解释,通俗的讲就是FactoryBean类型的对象返回的不是对象本身,而是FactoryBean中getObject()方法返回的值,可以看到CronTriggerFactoryBean中的这个方法实现
@Override @Nullable public CronTrigger getObject() { return this.cronTrigger; }
而 public interface CronTrigger extends Trigger, 即 cronTrigger是Trigger,这样,疑问就解释的通了。
本人对时间调度刚刚接触,错误之处欢迎指正。