我是靠谱客的博主 热情糖豆,最近开发中收集的这篇文章主要介绍解决Spring boot集成quartz时service注入失败为null的问题解决Spring boot集成quartz时service注入失败为null的问题,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
解决Spring boot集成quartz时service注入失败为null的问题
调度任务理框架选择
提及调度任务,一般使用过Spring boot的开发者首先会想起自带的调度框架,直接用Spring boot中的@Scheduled注解,但是在一般的商业实际应用当中,往往有复杂的业务逻辑需要处理,有些还需要调度任务的持久化,这种场景下Spring boot自带的调度任务则有点力不从心,此时需要考虑去集成别的调度框架,而Quartz就是一个应用场景广泛的且能解决Spring boot自带调度任务实现不了的功能的不二之选。
Quartz是OpenSymphony开源的一个项目,是一个由Java编写的开源作业调度框架。
Spring task
优点:无需整合spring,作业类中就可以调用业务service
缺点:单线程;不能做数据存储型的定时任务
Quartz
优点:多线程;可以做数据存储型的定时任务,维护性高;多任务情况下,quartz更容易管理,可以实现动态配置;
缺点:需要整合spring,不能直接调用业务层service;
Quartz特点
1、支持分布式高可用
我们需要某个定时任务在多个节点中只有某个节点可以执行时,就需要Quartz来实现,否则使用@Scheduled等方式会造成所有节点都执行一遍。
2、支持持久化
Quartz有专门的数据表来实现定时任务的持久化。
3、支持多任务调度和管理
Quartz可以在数据库中存储多个定时任务进行作业调度,可以实现定时任务的增删改查等管理。
选用原则
优先推荐使用Quartz调度以应对复杂的调度场景,除非要使用的场景简单且单一,甚至是一次性的,或者不需要持久化的可以考虑使用spring boot自带的调度框架以减少集成时的时间成本。
Spring boot 与quartz代码集成
pom依赖
<!--quartz定时调度依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--quartz定时调度依赖-->
<!--jdbc依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
quartz配置
新建quartz.properties
配置如下:
#[quartz批处理配置文件]
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#============================================================================
# Configure ThreadPool
线程池配置
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#持久化配置
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 50000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#支持集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.useProperties = true
org.quartz.jobStore.clusterCheckinInterval = 15000
#使用weblogic连接Oracle驱动
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = qzDS
#============================================================================
# Configure Datasources
#============================================================================
#数据源连接信息,quartz默认使用c3p0数据源可以被自定义数据源覆盖
org.quartz.dataSource.qzDS.connectionProvider.class=com.summer.msp.druid.conn.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = 123456
#配置数据库源(org.quartz.dataSource.qzDS.maxConnections: c3p0配置的是有s的,druid数据源没有s)
org.quartz.dataSource.qzDS.maxConnection = 10
java代码
1、新建MyJobFactory类
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName: MyJobFactory
* @Description: TODO
* @author Sebastian
* @date 2020年11月08日 上午10:42:28
* @version V1.0
*/
@Component
public class MyJobFactory extends AdaptableJobFactory {
//这个对象Spring会帮我们自动注入进来
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
//重写创建Job任务的实例方法
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
//通过以下方式,解决Job任务无法使用Spring中的Bean问题
autowireCapableBeanFactory.autowireBean(jobInstance);
//return super.createJobInstance(bundle);
return jobInstance;
}
}
2、新建QuartzConfiguration类
import org.quartz.Scheduler;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.io.IOException;
import java.util.Properties;
/**
* @ClassName: QuartzConfiguration
* @Description: TODO
* @author Sebastian
* @date 2020年11月08日 上午10:42:28
* @version V1.0
*/
@Configuration
@EnableScheduling
public class QuartzConfiguration {
@Autowired
private MyJobFactory myJobFactory;
//创建调度器工厂
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
//1.创建SchedulerFactoryBean
//2.加载自定义的quartz.properties配置文件
//3.设置MyJobFactory
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
try {
// 加载quartz数据源配置
factoryBean.setQuartzProperties(quartzProperties());
// 自定义Job Factory,用于Spring注入
factoryBean.setJobFactory(myJobFactory);
//启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factoryBean.setOverwriteExistingJobs(true);
// 延时启动
factoryBean.setStartupDelay(20);
return factoryBean;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 加载quartz数据源配置
*
* @return
* @throws IOException
*/
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean=new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/*
* quartz初始化监听器
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
@Bean(name="scheduler")
public Scheduler scheduler(){
return schedulerFactoryBean().getScheduler();
}
}
3、新建一个job实现类
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.summer.msp.core.dao.IBaseDao;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class ContractInfoReportJob implements Job{
private static org.slf4j.Logger logger = LoggerFactory.getLogger(ContractInfoReportJob.class);
@Autowired
private IBaseDao baseDao;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Map<String,Object> params = new HashMap<>();
params.put("username", "admin");
//进行业务处理,比如批量更新
Map<String,Object> res = baseDao.selectOne("sys_user_info.qryUserInfo",params);
System.out.println("Hello quzrtz
"+
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()));
}
}
新建scheduler调度
package com.summer.msp.batch.config;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
import com.summer.msp.batch.reportJob.ContractInfoReportJob;
/**
*
* @ClassName: CronSchedulerJob
* @Description: 主要配置定时任务的调度,把新建的job实现类进行唤起和持久化
* @author Sebastian
* @date 2020年11月08日 上午10:42:28
* @version V1.0
*/
@Component
public class CronSchedulerJob {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(CronSchedulerJob.class);
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
private void ContractInfoReportJob(Scheduler scheduler) throws SchedulerException {
JobKey jobkey = JobKey.jobKey("contractInfoReportJob", "reportGroup");
boolean flag = scheduler.checkExists(jobkey);
logger.info("scheduler存在标志为={}",flag);
String cronTime = "0 0/2 * * * ?";
if(!flag && jobkey == null) {
//没有任务时新建任务
JobDetail jobDetail = JobBuilder.newJob(ContractInfoReportJob.class)
/**给当前JobDetail添加参数,K V形式,链式调用,可以传入多个参数,在Job实现类中,可以通过jobExecutionContext.getJobDetail().getJobDataMap().get("age")获取值*/
//.usingJobData("age",23)
/**添加认证信息,有3种重写的方法,我这里是其中一种,可以查看源码看其余2种*/
.withIdentity("contractInfoReportJob", "reportGroup").build();
// 6的倍数秒执行 也就是 6 12 18 24 30 36 42 ....
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronTime);
Trigger trigger = TriggerBuilder.newTrigger()
/**给当前JobDetail添加参数,K V形式,链式调用,可以传入多个参数,在Job实现类中,可以通过jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")获取值*/
//.usingJobData("orderNo", "123456")
.withIdentity("contractInfoReportTrigger", "reportTriggerGroup").startNow().withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
if(!scheduler.isShutdown()) {
scheduler.start();
}
}else {
TriggerKey triggerKey = new TriggerKey("contractInfoReportTrigger", "reportTriggerGroup");
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
String oldTime = cronTrigger.getCronExpression();
//此处主要更新已存在的时间表达式
if (!oldTime.equalsIgnoreCase(cronTime)) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronTime);
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("contractInfoReportTrigger", "reportTriggerGroup")
.withSchedule(cronScheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
}
logger.info("==================================更新Job成功!==================================");
if(flag && !scheduler.isShutdown()) {
scheduler.start();
}
}
}
/**
* 同时启动多个定时任务,这里将本类中定义的Jobscheduler统一放在这里启动了
**/
public void scheduleJobs() throws SchedulerException {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
//此处可以配置多个,在这个类里新建Job(Scheduler scheduler)方法
ContractInfoReportJob(scheduler);
}
}
至此Spring Cloud集成quartz调度结束。
几个常见问题解决办法
1、service注入失败解决方法
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName: MyJobFactory
* @Description: TODO
* @author Sebastian
* @date 2020年11月08日 上午10:42:28
* @version V1.0
*/
@Component
public class MyJobFactory extends AdaptableJobFactory {
//这个对象Spring会帮我们自动注入进来
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
//重写创建Job任务的实例方法
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
//通过以下方式,解决Job任务无法使用Spring中的Bean问题
autowireCapableBeanFactory.autowireBean(jobInstance);
//return super.createJobInstance(bundle);
return jobInstance;
}
}
2、调度任务随项目启动
方式一、在QuartzConfiguration类中加上这段方法,并将CronSchedulerJob注入,利用定时启动quratz
@Autowired
private CronSchedulerJob cronSchedulerJob;
@Scheduled(cron="0 0/2 * * * ?")
public void startScheduler() {
System.out.println("====================================== quartz调度器启动完成 ======================================");
try {
cronSchedulerJob.scheduleJobs();
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
方式二、配置监听类QuartzStartListener ,将CronSchedulerJob 依赖注入到监听类中,随项目启动
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* @ClassName: QuartzStartListener
* @Description:quartz监听服务,启动服务后,调用已经注册好的job,开启调度器
* @author Sebastian
* @date 2020年11月19日 上午10:42:28
* @version V1.0
*/
@Configuration
public class QuartzStartListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private CronSchedulerJob cronSchedulerJob;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
System.out.println("====================================== quartz调度器启动完成 ======================================");
cronSchedulerJob.scheduleJobs();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
如果用我这种方法不能解决依赖注入service问题,可以找下以下
链接:https://www.freesion.com/article/7227177376/
最后
以上就是热情糖豆为你收集整理的解决Spring boot集成quartz时service注入失败为null的问题解决Spring boot集成quartz时service注入失败为null的问题的全部内容,希望文章能够帮你解决解决Spring boot集成quartz时service注入失败为null的问题解决Spring boot集成quartz时service注入失败为null的问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复