Check the Friday before the Spring boot
integration Quartz
project and found configuration errors, so by reading the source code way to explore Spring
correct integrated Quartz
way.
Problems found
Check the last year of the project code, found on QuartzJobBean
the existence realized unreasonable.
(1) project dependencies:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
</dependencies>
(2) the issue of code:
@Component
public class UnprocessedTaskJob extends QuartzJobBean {
private TaskMapper taskMapper;
@Autowired
public UnprocessedTaskJob(TaskMapper taskMapper){
this.taskMapper = taskMapper;
}
}
private JobDetail generateUnprocessedJobDetail(Task task) {
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put(UnprocessedTaskJob.TASK_ID, task.getId());
return JobBuilder.newJob(UnprocessedTaskJob.class)
.withIdentity(UnprocessedTaskJob.UNPROCESSED_TASK_KEY_PREFIX + task.getId(), UnprocessedTaskJob.UNPROCESSED_TASK_JOB_GROUP)
.usingJobData(jobDataMap)
.storeDurably()
.build();
}
(3) refining questions:
Causes of errors than code, UnprocessedTaskJob
add @Component
annotations, which represents a Spring IOC
container 单例
class.
However, Quartz
created Job
by respective Quartz Job Bean
of class
reflection to create appropriate Job
. That is, each time creating a new Job
time, will yield the corresponding Job
examples. Thereby , which UnprocessedTaskJob
is 单例
in conflict.
View Code to submit records, because at that time that does not add @Component
annotations, you can not through @Autowired
the introduction of a Spring IOC
hosted taskMapper
, that can not be achieved instance 依赖注入
.
However, surprising is that when I go in the development environment in addition to UnprocessedTaskJob
the @Component
following notes, after running the program finds TaskMapper
instance can still be injected into Job
the program running ...
Spring-managed Quartz
Code Analysis
Online search Spring
hosting Quartz
articles, most of Spring MVC
the project, focusing on how to solve the Job
implementation class through the @Autowired
realization Spring
of 依赖注入
.
Most rely on the Internet to achieve SpringBeanJobFactory
to achieve Spring
the Quartz
integration.
/**
* Subclass of {@link AdaptableJobFactory} that also supports Spring-style
* dependency injection on bean properties. This is essentially the direct
* equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz
* {@link org.quartz.spi.JobFactory}.
*
* <p>Applies scheduler context, job data map and trigger data map entries
* as bean property values. If no matching bean property is found, the entry
* is by default simply ignored. This is analogous to QuartzJobBean's behavior.
*
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.0
* @see SchedulerFactoryBean#setJobFactory
* @see QuartzJobBean
*/
public class SpringBeanJobFactory extends AdaptableJobFactory
implements ApplicationContextAware, SchedulerContextAware {
}
/**
* {@link JobFactory} implementation that supports {@link java.lang.Runnable}
* objects as well as standard Quartz {@link org.quartz.Job} instances.
*
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.0
* @see DelegatingJob
* @see #adaptJob(Object)
*/
public class AdaptableJobFactory implements JobFactory {
}
It can be found by the above codes, and comments:
(1) AdaptableJobFactory
implements JobFactory
the interface, you can take to create a standard Quartz
instance (only Quartz
2.1.4 and above);
(2) SpringBeanJobFactory
inherited AdaptableJobFactory
to achieve the Quartz
properties dependent package instance injection.
(3) SpringBeanJobFactory
implements ApplicationContextAware
and SchedulerContextAware
interfaces ( Quartz
task scheduling context), it can be created in Job Bean
the injection time ApplicationContex
and SchedulerContext
.
Tips:
The above code based Spring
5.1.8 version
in Spring 4.1.0
version, SpringBeanJobFactory
implemented as shown in the following code:
public class SpringBeanJobFactory extends AdaptableJobFactory
implements SchedulerContextAware{
// 具体代码省略
}
Thus, in the early Spring
project, the need to package SpringBeanJobFactory
and implement ApplicationContextAware
the interface (no surprise surprise?).
Spring old version Solutions
Based on older versions Spring
give a solution to Spring
integrated Quartz
solutions.
Solutions from the thirty-ninth chapter: Based on the timing of the completion of the task SpringBoot & Quartz distributed single-node persistence provided (series quality Great God is great).
@Configuration
public class QuartzConfiguration
{
/**
* 继承org.springframework.scheduling.quartz.SpringBeanJobFactory
* 实现任务实例化方式
*/
public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
/**
* 将job实例交给spring ioc托管
* 我们在job实例实现类内可以直接使用spring注入的调用被spring ioc管理的实例
* @param bundle
* @return
* @throws Exception
*/
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
/**
* 将job实例交付给spring ioc
*/
beanFactory.autowireBean(job);
return job;
}
}
/**
* 配置任务工厂实例
* @param applicationContext spring上下文实例
* @return
*/
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext)
{
/**
* 采用自定义任务工厂 整合spring实例来完成构建任务
* see {@link AutowiringSpringBeanJobFactory}
*/
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* 配置任务调度器
* 使用项目数据源作为quartz数据源
* @param jobFactory 自定义配置任务工厂(其实就是AutowiringSpringBeanJobFactory)
* @param dataSource 数据源实例
* @return
* @throws Exception
*/
@Bean(destroyMethod = "destroy",autowire = Autowire.NO)
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception
{
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//将spring管理job自定义工厂交由调度器维护
schedulerFactoryBean.setJobFactory(jobFactory);
//设置覆盖已存在的任务
schedulerFactoryBean.setOverwriteExistingJobs(true);
//项目启动完成后,等待2秒后开始执行调度器初始化
schedulerFactoryBean.setStartupDelay(2);
//设置调度器自动运行
schedulerFactoryBean.setAutoStartup(true);
//设置数据源,使用与项目统一数据源
schedulerFactoryBean.setDataSource(dataSource);
//设置上下文spring bean name
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
//设置配置文件位置
schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
return schedulerFactoryBean;
}
}
By the above code, it is achieved by SpringBeanJobFactory
the createJobInstance
created Job
instance, and the resulting Job
instance by the delivery AutowireCapableBeanFactory
to host.
schedulerFactoryBean
Is set as JobFactory
(in fact AutowiringSpringBeanJobFactory
, the inside of the package applicationContext
), and DataSource
(a data source, if not set, the Quartz
default RamJobStore
).
RamJobStore
The advantage is fast, the disadvantage is that scheduling tasks can not persistently saved.
Therefore, we can use within the regular task Spring IOC
of @Autowired
notes like 依赖注入
.
The new version of the Spring Solutions
(1) explain
If you are using Spring boot
, and the version is greater than the good 2.0
, it is recommended spring-boot-starter-quartz
.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
Auto-configuration support is now include for the Quartz Scheduler. We’ve also added a new spring-boot-starter-quartz starter POM.
You can use in-memory JobStores, or a full JDBC-based store. All JobDetail, Calendar and Trigger beans from your Spring application context will be automatically registered with the Scheduler.
For more details read the new “Quartz Scheduler” section of the reference documentation.
These are the spring-boot-starter-quartz
introduction, based on the presentation shows that if you do not turn off Quartz
the auto-configuration, SpringBoot
will help you complete Scheduler
the automated configuration, such as JobDetail
/ Calendar
/ Trigger
, etc. Bean
will be automatically registered to Shceduler
in. You can QuartzJobBean
use the free @Autowired
and other 依赖注入
annotations.
In fact, does not introduce spring-boot-starter-quartz
, but only imported org.quartz-scheduler
, Quartz
automated configuration will still take effect (this is the first analysis of the problem, remove the @Bean
comments, the program is still running the reasons, tragedy lucky).
(2) Analysis Code
/**
* {@link EnableAutoConfiguration Auto-configuration} for Quartz Scheduler.
*
* @author Vedran Pavic
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class })
@EnableConfigurationProperties(QuartzProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class QuartzAutoConfiguration{
// 此处省略部分代码
@Bean
@ConditionalOnMissingBean
public SchedulerFactoryBean quartzScheduler() {
// 因为新版本SchedulerFactoryBean已经实现ApplicationContextAware接口
// 因此相对于老版本Spring解决方案中的AutowiringSpringBeanJobFactory进行封装
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
// SpringBeanJobFactory中注入applicationContext,为依赖注入创造条件
jobFactory.setApplicationContext(this.applicationContext);
// schedulerFactoryBean中注入setJobFactory(注意此处没有配置DataSource,DataSource详见`JdbcStoreTypeConfiguration`)
// 以上这几个步骤,与老版本的Spring解决方案类似
schedulerFactoryBean.setJobFactory(jobFactory);
// 后续都是Quartz的配置属性设置,不再叙述
if (this.properties.getSchedulerName() != null) {
schedulerFactoryBean.setSchedulerName(this.properties.getSchedulerName());
}
schedulerFactoryBean.setAutoStartup(this.properties.isAutoStartup());schedulerFactoryBean.setStartupDelay((int) this.properties.getStartupDelay().getSeconds());
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(this.properties.isWaitForJobsToCompleteOnShutdown());
schedulerFactoryBean.setOverwriteExistingJobs(this.properties.isOverwriteExistingJobs());
if (!this.properties.getProperties().isEmpty()) {
schedulerFactoryBean.setQuartzProperties(asProperties(this.properties.getProperties()));
}
if (this.jobDetails != null && this.jobDetails.length > 0) {
schedulerFactoryBean.setJobDetails(this.jobDetails);
}
if (this.calendars != null && !this.calendars.isEmpty()) {
schedulerFactoryBean.setCalendars(this.calendars);
}
if (this.triggers != null && this.triggers.length > 0) {
schedulerFactoryBean.setTriggers(this.triggers);
}
customize(schedulerFactoryBean);
return schedulerFactoryBean;
}
@Configuration
@ConditionalOnSingleCandidate(DataSource.class)
protected static class JdbcStoreTypeConfiguration {
// 为Quartz的持久化配置DataSource,具体代码可以翻阅Spring源码得到
}
}
Next, the SpringBeanJobFactory
analysis, which generated Job
instance, and performing 依赖注入
key operation type.
/**
* Subclass of {@link AdaptableJobFactory} that also supports Spring-style
* dependency injection on bean properties. This is essentially the direct
* equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz
* {@link org.quartz.spi.JobFactory}.
*
* <p>Applies scheduler context, job data map and trigger data map entries
* as bean property values. If no matching bean property is found, the entry
* is by default simply ignored. This is analogous to QuartzJobBean's behavior.
*
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.0
* @see SchedulerFactoryBean#setJobFactory
* @see QuartzJobBean
*/
public class SpringBeanJobFactory extends AdaptableJobFactory
implements ApplicationContextAware, SchedulerContextAware {
@Nullable
private String[] ignoredUnknownProperties;
@Nullable
private ApplicationContext applicationContext;
@Nullable
private SchedulerContext schedulerContext;
/**
* Specify the unknown properties (not found in the bean) that should be ignored.
* <p>Default is {@code null}, indicating that all unknown properties
* should be ignored. Specify an empty array to throw an exception in case
* of any unknown properties, or a list of property names that should be
* ignored if there is no corresponding property found on the particular
* job class (all other unknown properties will still trigger an exception).
*/
public void setIgnoredUnknownProperties(String... ignoredUnknownProperties) {
this.ignoredUnknownProperties = ignoredUnknownProperties;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void setSchedulerContext(SchedulerContext schedulerContext) {
this.schedulerContext = schedulerContext;
}
/**
* Create the job instance, populating it with property values taken
* from the scheduler context, job data map and trigger data map.
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 创建Job实例
// (1) 包含applicationContext,则通过AutowireCapableBeanFactory()创建相应Job实例,实现依赖注入
// (2) 如果applicationContext为空,则使用AdaptableJobFactory创建相应的Bean(无法实现依赖注入)
Object job = (this.applicationContext != null ?
this.applicationContext.getAutowireCapableBeanFactory().createBean(
bundle.getJobDetail().getJobClass(), AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false) :
super.createJobInstance(bundle));
if (isEligibleForPropertyPopulation(job)) {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
MutablePropertyValues pvs = new MutablePropertyValues();
if (this.schedulerContext != null) {
pvs.addPropertyValues(this.schedulerContext);
}
pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
if (this.ignoredUnknownProperties != null) {
for (String propName : this.ignoredUnknownProperties) {
if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
pvs.removePropertyValue(propName);
}
}
bw.setPropertyValues(pvs);
}
else {
bw.setPropertyValues(pvs, true);
}
}
return job;
}
// 省略部分代码
}
/**
* {@link JobFactory} implementation that supports {@link java.lang.Runnable}
* objects as well as standard Quartz {@link org.quartz.Job} instances.
*
* <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
*
* @author Juergen Hoeller
* @since 2.0
* @see DelegatingJob
* @see #adaptJob(Object)
*/
public class AdaptableJobFactory implements JobFactory {
/**
* Create an instance of the specified job class.
* <p>Can be overridden to post-process the job instance.
* @param bundle the TriggerFiredBundle from which the JobDetail
* and other info relating to the trigger firing can be obtained
* @return the job instance
* @throws Exception if job instantiation failed
*/
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 获取`QuartzJobBean`的实现`class`,通过反射工具创建相应的类实例(自然无法注入Spring托管的Bean实例)
Class<?> jobClass = bundle.getJobDetail().getJobClass();
return ReflectionUtils.accessibleConstructor(jobClass).newInstance();
}
}
Here you need to explain AutowireCapableBeanFactory
action.
Projects, and some did not achieve Spring
integration depth, it is not examples of Spring
container management.
However, the need for which has not been Spring
managed in Bean
the need to introduce Spring
the container Bean
.
At this time, We need to achieve AutowireCapableBeanFactory
, so that Spring
dependency injection and other functions.
We hope the above explanation and code analysis, let you know how the old version and the new version Spring
integrated right in Quartz
.
In addition, Spring boot
automated configuration can resolve the vast majority of configuration issues, but in plenty of time, it is recommended by reading the source code, etc. understanding 配置细节
, thereby leading to more confidence.
PS:
If you think my article helpful to you, you can scan the code to receive support under a red envelope or scan code (random number, a penny is love), thank you!
Alipay red envelope | Alipay | Micro letter |
---|---|---|