简单的定时任务控制,注解使用spring自带定时任务就可以完成啦,定时任务类也是由Spring管理,所以可以正常使用IOC自动注入其他的类.
具体原理没有深究哈,日后学习Spring源码的时候再详细了解吧.这里只说怎么应用.
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
这个@Slf4j是用了lombok自动生成了一个log,没有配置的话可以直接创建一个log
这里是初始延迟1s,然后以固定3s的频率执行,在方法中sleep了5s.
也可以直接使用cron表达式,这里简单示例
@Component
@Slf4j
public class TestScheduled {
@Scheduled(fixedRateString = "3000", initialDelay = 1000)
public void printTrace() {
log.info("printTrace---------------------------------------- at" + LocalTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
会看到程序的打印输出的时间大概是这样:
printTrace--- at11:30:03.713384
printTrace--- at11:30:10.923850
printTrace--- at11:30:17.945608
printTrace--- at11:30:24.957384
你可能会疑问,这时间间隔并不是3s啊,这什么玩意?
这是因为Scheduled默认是同步执行的,下次一任务来的时候,发现上一个还没执行完,那就等它执行完再执行.
然后在TestScheduled类上加注解@Async
然后我们打印一下当前线程,便于观察:
log.info("printTrace-- at" + LocalTime.now() + "in thread:" + Thread.currentThread().getName());
就会发现每隔3s打印一次,8个线程循环执行,大概就是默认线程池的coreSize是8了,以后再详细研究.
在主启动类中,或者其他配置类中,加入一个Bean:
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.initialize();
// 试了下这个,不太好使,先不深入探究了..
/*Executor executor = new ThreadPoolExecutor(
2
, 10
, 2000
, TimeUnit.MILLISECONDS
, new ArrayBlockingQueue<>(100)
, Executors.defaultThreadFactory()
, new ThreadPoolExecutor.AbortPolicy()
);*/
return executor;
}
先参照W3C了解quartz的基本用法.
然后会发现,应用到Spring项目中,Job中不能使用Spring管理的对象,IOC自动注入类.所以需要稍微做点配置.
org.quartz-scheduler
quartz
2.2.3
org.quartz-scheduler
quartz-jobs
2.2.3
org.springframework
spring-context-support
定时任务的信息一般会配置存储在数据库中,demo也可以存储在内存中
org.quartz.scheduler.instanceName=Test-testScheduler
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName: TestScheduler
org.quartz.scheduler.instanceId: AUTO
org.quartz.scheduler.skipUpdateCheck: true
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 5
org.quartz.threadPool.threadPriority: 5
#============================================================================
# Configure JobStore
#============================================================================
#容许的最大作业延
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=springTxDataSource.schedulerFactoryBean
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
#调度实例失效的检查时间间隔
org.quartz.jobStore.clusterCheckinInterval=20000
在spring的配置文件(application.properties)中配置quartz配置文件的路径,方便灵活调试:
quartzConfig.location=config/quartz-prod.properties
package weizhi.example.hiquartz.config;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.sql.DataSource;
/**
* @Auther: liweizhi
* @Date: 2019/6/24 14:53
* @Description:
*/
@Configuration
// basePackages指定mapper的文件夹,如果需要主动查询数据库中的内容,不需要的话可以不配置
@MapperScan(basePackages = {"weizhi.example.hiquartz.dao.quartzdb"}, sqlSessionTemplateRef = "sqlSessionTemplatequartz")
public class DatasourceConfigQuartzDb {
@Bean(name = "quartzDataSource")
public DataSource quartzDb(Environment env) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(env.getProperty("spring.datasource.quartzdb.url"));
ds.setUsername(env.getProperty("spring.datasource.quartzdb.username"));
ds.setPassword(env.getProperty("spring.datasource.quartzdb.password"));
ds.setDriverClassName(env.getProperty("spring.datasource.quartzdb.driver-class-name"));
ds.setMaximumPoolSize(10);
return ds;
}
@Bean
public SqlSessionFactory sqlSessionFactoryquartz(@Qualifier("quartzDataSource") @Autowired DataSource quartzDb) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(quartzDb);
// sql写在mapper.xml中需配置
// factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("mapperLocation"));
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplatequartz(SqlSessionFactory sqlSessionFactoryquartz) throws Exception {
return new SqlSessionTemplate(sqlSessionFactoryquartz);
}
}
@Component
public class AutoWiredSpringBeanToJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
package weizhi.example.hiquartz.config;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import weizhi.example.hiquartz.config.component.AutoWiredSpringBeanToJobFactory;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* @author liweizhi
* @date 2019/11/13 16:35
*/
@Configuration
public class SchedulerConfig {
// 配置文件路径
@Value("${quartzConfig.location}")
private String quartzConfig;
// 按照自己注入的数据源自行修改
@Qualifier("quartzDataSource")
@Autowired
private DataSource quartzDataSource;
@Autowired
private AutoWiredSpringBeanToJobFactory autoWiredSpringBeanToJobFactory;
/**
* 从quartz.properties文件中读取Quartz配置属性
*
* @return
* @throws IOException
*/
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
// new ClassPathResource(QUARTZ_CONFIG)
propertiesFactoryBean.setLocation(new FileSystemResource(quartzConfig));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/**
* JobFactory与schedulerFactoryBean中的JobFactory相互依赖,注意bean的名称
* 在这里为JobFactory注入了Spring上下文
*
* @param applicationContext
* @return
*/
@Bean
public JobFactory buttonJobFactory(ApplicationContext applicationContext) {
AutoWiredSpringBeanToJobFactory jobFactory = new AutoWiredSpringBeanToJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(autoWiredSpringBeanToJobFactory);
factory.setOverwriteExistingJobs(true);
// 设置自行启动
factory.setAutoStartup(true);
// 延时启动,应用启动1秒后
factory.setStartupDelay(1);
factory.setQuartzProperties(quartzProperties());
// 使用应用的dataSource替换quartz的dataSource
factory.setDataSource(quartzDataSource);
return factory;
}
@Bean(name = "myScheduler")
public Scheduler createScheduler(SchedulerFactoryBean schedulerFactoryBean)
throws IOException, SchedulerException {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.start();
return scheduler;
}
}
不需要加@Component,直接可以使用@Autowired自动注入
@Slf4j
public class HiJob implements Job {
@Autowired
private IPetService petService;
public HiJob() {
}
@Override
public void execute(JobExecutionContext context) {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
log.info("getted param name:{}, JobDetail.Key:{}", jobDataMap.getString("name"), context.getJobDetail().getKey());
petService.getAllPet();
}
}
/**
* @author liweizhi
* @date 2019/11/13 16:23
*/
@RestController
@RequestMapping("quartz")
public class QuartzController {
private static final String GROUP = "group1";
@Autowired
@Qualifier("myScheduler")
private Scheduler scheduler;
@GetMapping("meta")
public SchedulerMetaData getMetaData() throws SchedulerException {
return scheduler.getMetaData();
}
@GetMapping("/exist/{id}")
public boolean existJob(@PathVariable("id") String id) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(id, GROUP);
return scheduler.checkExists(jobKey);
}
@GetMapping("/delete/{id}")
public SchedulerMetaData deleteJob(@PathVariable("id") String id) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(id, GROUP);
scheduler.deleteJob(jobKey);
return scheduler.getMetaData();
}
@GetMapping("/add/{id}")
public SchedulerMetaData addJob(@PathVariable("id") String id) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(id, GROUP);
JobDetail job = JobBuilder.newJob(HiJob.class)
.withIdentity(jobKey)
.usingJobData("name", "=============axiba================")
.build();
// Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(TriggerKey.triggerKey(id, GROUP))
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(5000)
.repeatForever()
)
.startAt(new Date(System.currentTimeMillis() + 5000))
/*.withSchedule(cronSchedule("0/20 * * ? * *")
// .withMisfireHandlingInstructionDoNothing()
)*/
.build();
Trigger triggerRightNow = TriggerBuilder.newTrigger()
.withIdentity(TriggerKey.triggerKey(id + "::triggerRightNow", GROUP))
.forJob(jobKey)
.startNow()
.build();
System.err.println("before scheduleJob =============" + new Date());
scheduler.scheduleJob(job, trigger);
// scheduler.scheduleJob(triggerRightNow);
System.err.println("after scheduleJob =============" + new Date());
return scheduler.getMetaData();
}
}
@SpringBootApplication
@PropertySource("file:config/application.properties")
public class HiquartzApplication {
public static void main(String[] args) {
SpringApplication.run(HiquartzApplication.class, args);
}
}