在 Spring 中将 bean 引用注入到 Quartz 作业中?
我设法在 Spring 中使用 JobStoreTX 持久存储来配置和调度 Quartz 作业。我不使用 Spring 的 Quartz 作业,因为我需要在运行时动态调度它们,并且我发现的所有将 Spring 与 Quartz 集成的示例都是在 Spring 配置文件中对 shcedule 进行硬编码...无论如何,这里是如何实现的我安排了该作业:
JobDetail emailJob = JobBuilder.newJob(EMailJob.class)
.withIdentity("someJobKey", "immediateEmailsGroup")
.storeDurably()
.build();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity("someTriggerKey", "immediateEmailsGroup")
.startAt(fireTime)
.build();
// pass initialization parameters into the job
emailJob.getJobDataMap().put(NotificationConstants.MESSAGE_PARAMETERS_KEY, messageParameters);
emailJob.getJobDataMap().put(NotificationConstants.RECIPIENT_KEY, recipient);
if (!scheduler.checkExists(jobKey) && scheduler.getTrigger(triggerKey) != null) {
// schedule the job to run
Date scheduleTime1 = scheduler.scheduleJob(emailJob, trigger);
}
EMailJob 是一个简单的作业,它使用 Spring 的 JavaMailSenderImpl 类发送电子邮件。
public class EMailJob implements Job {
@Autowired
private JavaMailSenderImpl mailSenderImpl;
public EMailJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
....
try {
mailSenderImpl.send(mimeMessage);
} catch (MessagingException e) {
....
throw new JobExecutionException("EMailJob failed: " + jobKey.getName(), e);
}
logger.info("EMailJob finished OK");
}
问题是我需要在我的 EMailJob 类中获取对该类 (JavaMailSenderImpl) 实例的引用。当我尝试像这样注入它时:
@Autowired
private JavaMailSenderImpl mailSenderImpl;
它没有注入 - 引用为 NULL。我假设发生这种情况是因为实例化 EMailJob 类的不是 Spring,而是 Quartz,而 Quartz 对依赖注入一无所知……
那么,有什么方法可以强制这种注入发生吗?
谢谢!
更新1: @亚伦: 这是启动时堆栈跟踪的相关部分,它显示 EMailJob 被实例化两次:
2011-08-15 14:16:38,687 [main] INFO org.springframework.context.support.GenericApplicationContext - Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler#0' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2011-08-15 14:16:38,734 [main] INFO org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1328c7a: defining beans [...]; root of factory hierarchy
2011-08-15 14:16:39,734 [main] INFO com.cambridgedata.notifications.EMailJob - EMailJob() - initializing ...
2011-08-15 14:16:39,937 [main] INFO org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Validated configuration attributes
2011-08-15 14:16:40,078 [main] INFO org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Validated configuration attributes
2011-08-15 14:16:40,296 [main] INFO org.springframework.jdbc.datasource.init.ResourceDatabasePopulator - Executing SQL script from class path resource ...
2011-08-15 14:17:14,031 [main] INFO com.mchange.v2.log.MLog - MLog clients using log4j logging.
2011-08-15 14:17:14,109 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
2011-08-15 14:17:14,171 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2011-08-15 14:17:14,171 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.0.1 created.
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
2011-08-15 14:17:14,187 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.0.1) 'NotificationsScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'NotificationsScheduler' initialized from the specified file : 'spring/quartz.properties' from the class resource path.
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.0.1
2011-08-15 14:17:14,234 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2sajb28h1lcabf28k3nr1|13af084, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2sajb28h1lcabf28k3nr1|13af084, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/2010rewrite2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> select 0 from dual, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2011-08-15 14:17:14,312 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
2011-08-15 14:17:14,328 [main] INFO org.quartz.core.QuartzScheduler - Scheduler NotificationsScheduler_$_NON_CLUSTERED started.
2011-08-15 14:17:14,515 [NotificationsScheduler_QuartzSchedulerThread] INFO com.cambridgedata.notifications.EMailJob - EMailJob() - initializing ...
谢谢!
更新#2:@Ryan:
我尝试按如下方式使用 SpringBeanJobFactory:
<bean id="jobFactoryBean" class="org.springframework.scheduling.quartz.SpringBeanJobFactory">
</bean>
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:spring/quartz.properties"/>
<property name="jobFactory" ref="jobFactoryBean"/>
</bean>
并且我已经修改了我的主类以从该工厂获取 Scheduler,而不是 Quartz':
@PostConstruct
public void initNotificationScheduler() {
try {
//sf = new StdSchedulerFactory("spring/quartz.properties");
//scheduler = sf.getScheduler();
scheduler = schedulerFactoryBean.getScheduler();
scheduler.start();
....
但是当我运行应用程序时 - 出现错误,请参见下文。这是 Spring 启动时的堆栈跟踪。看起来 Scheduler 本身创建得很好,但是当它尝试实例化我的 EMailJob 时出现错误:
2011-08-15 21:49:42,968 [main] INFO org.springframework.scheduling.quartz.SchedulerFactoryBean - Loading Quartz config from [class path resource [spring/quartz.properties]]
2011-08-15 21:49:43,031 [main] INFO com.mchange.v2.log.MLog - MLog clients using log4j logging.
2011-08-15 21:49:43,109 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.0.1 created.
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.0.1) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.0.1
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.QuartzScheduler - JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@566633
2011-08-15 21:49:43,265 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge13f8h1lsg7py1rg0iu0|1956391, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge13f8h1lsg7py1rg0iu0|1956391, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/2010rewrite2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> select 0 from dual, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2011-08-15 21:49:43,343 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
2011-08-15 21:49:43,359 [main] INFO org.quartz.core.QuartzScheduler - Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
2011-08-15 21:49:43,562 [schedulerFactoryBean_QuartzSchedulerThread] ERROR org.quartz.core.ErrorLogger - An error occured instantiating job to be executed. job= 'immediateEmailsGroup.DEFAULT.jobFor_1000new1'
org.quartz.SchedulerException: Problem instantiating class 'com.cambridgedata.notifications.EMailJob' - [See nested exception: java.lang.AbstractMethodError: org.springframework.scheduling.quartz.SpringBeanJobFactory.newJob(Lorg/quartz/spi/TriggerFiredBundle;Lorg/quartz/Scheduler;)Lorg/quartz/Job;]
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:141)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:381)
Caused by: java.lang.AbstractMethodError: org.springframework.scheduling.quartz.SpringBeanJobFactory.newJob(Lorg/quartz/spi/TriggerFiredBundle;Lorg/quartz/Scheduler;)Lorg/quartz/Job;
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:134)
谢谢!
I managed to configure and schedule a Quartz job using JobStoreTX persistent store in Spring. I do not use Spring's Quartz jobs, because I need to schedule them dynamically, at run time, and all examples of integrating Spring with Quartz that i found were hard-coding the shcedules in the Spring config files... Anyway, here is how I schedule the job:
JobDetail emailJob = JobBuilder.newJob(EMailJob.class)
.withIdentity("someJobKey", "immediateEmailsGroup")
.storeDurably()
.build();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity("someTriggerKey", "immediateEmailsGroup")
.startAt(fireTime)
.build();
// pass initialization parameters into the job
emailJob.getJobDataMap().put(NotificationConstants.MESSAGE_PARAMETERS_KEY, messageParameters);
emailJob.getJobDataMap().put(NotificationConstants.RECIPIENT_KEY, recipient);
if (!scheduler.checkExists(jobKey) && scheduler.getTrigger(triggerKey) != null) {
// schedule the job to run
Date scheduleTime1 = scheduler.scheduleJob(emailJob, trigger);
}
The EMailJob is a simple job that is sending e-mail using the Spring's JavaMailSenderImpl class.
public class EMailJob implements Job {
@Autowired
private JavaMailSenderImpl mailSenderImpl;
public EMailJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
....
try {
mailSenderImpl.send(mimeMessage);
} catch (MessagingException e) {
....
throw new JobExecutionException("EMailJob failed: " + jobKey.getName(), e);
}
logger.info("EMailJob finished OK");
}
The problem is that I need to get a reference to an instance of this class (JavaMailSenderImpl) in my EMailJob class. When I try to inject it like this:
@Autowired
private JavaMailSenderImpl mailSenderImpl;
it is not injected - the reference is NULL. I'm assuming this is happening because it is not Spring who instantiates the EMailJob class, but Quartz, and Quartz does not know anything about dependency injection...
So, is there some way to force this injection to happen?
thanks!
Update 1:
@Aaron:
here is a relevant part of the stacktrace from the startup, which is showing the the EMailJob was instantiated twice:
2011-08-15 14:16:38,687 [main] INFO org.springframework.context.support.GenericApplicationContext - Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler#0' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2011-08-15 14:16:38,734 [main] INFO org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1328c7a: defining beans [...]; root of factory hierarchy
2011-08-15 14:16:39,734 [main] INFO com.cambridgedata.notifications.EMailJob - EMailJob() - initializing ...
2011-08-15 14:16:39,937 [main] INFO org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Validated configuration attributes
2011-08-15 14:16:40,078 [main] INFO org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Validated configuration attributes
2011-08-15 14:16:40,296 [main] INFO org.springframework.jdbc.datasource.init.ResourceDatabasePopulator - Executing SQL script from class path resource ...
2011-08-15 14:17:14,031 [main] INFO com.mchange.v2.log.MLog - MLog clients using log4j logging.
2011-08-15 14:17:14,109 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
2011-08-15 14:17:14,171 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2011-08-15 14:17:14,171 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.0.1 created.
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
2011-08-15 14:17:14,187 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.0.1) 'NotificationsScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'NotificationsScheduler' initialized from the specified file : 'spring/quartz.properties' from the class resource path.
2011-08-15 14:17:14,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.0.1
2011-08-15 14:17:14,234 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2sajb28h1lcabf28k3nr1|13af084, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2sajb28h1lcabf28k3nr1|13af084, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/2010rewrite2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> select 0 from dual, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2011-08-15 14:17:14,312 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
2011-08-15 14:17:14,328 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
2011-08-15 14:17:14,328 [main] INFO org.quartz.core.QuartzScheduler - Scheduler NotificationsScheduler_$_NON_CLUSTERED started.
2011-08-15 14:17:14,515 [NotificationsScheduler_QuartzSchedulerThread] INFO com.cambridgedata.notifications.EMailJob - EMailJob() - initializing ...
thanks!
Update #2: @Ryan:
I tried to use the SpringBeanJobFactory as following:
<bean id="jobFactoryBean" class="org.springframework.scheduling.quartz.SpringBeanJobFactory">
</bean>
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:spring/quartz.properties"/>
<property name="jobFactory" ref="jobFactoryBean"/>
</bean>
And I have modified my main class to get Scheduler from this factory, instead of Quartz':
@PostConstruct
public void initNotificationScheduler() {
try {
//sf = new StdSchedulerFactory("spring/quartz.properties");
//scheduler = sf.getScheduler();
scheduler = schedulerFactoryBean.getScheduler();
scheduler.start();
....
But when I run the app - get errors, see below. Here is the stacktrace from Spring startup . Seems like the Scheduler itself is created fine, but the error comes when it is trying to instantiate my EMailJob:
2011-08-15 21:49:42,968 [main] INFO org.springframework.scheduling.quartz.SchedulerFactoryBean - Loading Quartz config from [class path resource [spring/quartz.properties]]
2011-08-15 21:49:43,031 [main] INFO com.mchange.v2.log.MLog - MLog clients using log4j logging.
2011-08-15 21:49:43,109 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.0.1 created.
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.0.1) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
2011-08-15 21:49:43,187 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.0.1
2011-08-15 21:49:43,187 [main] INFO org.quartz.core.QuartzScheduler - JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@566633
2011-08-15 21:49:43,265 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge13f8h1lsg7py1rg0iu0|1956391, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge13f8h1lsg7py1rg0iu0|1956391, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/2010rewrite2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> select 0 from dual, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2011-08-15 21:49:43,343 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
2011-08-15 21:49:43,359 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
2011-08-15 21:49:43,359 [main] INFO org.quartz.core.QuartzScheduler - Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
2011-08-15 21:49:43,562 [schedulerFactoryBean_QuartzSchedulerThread] ERROR org.quartz.core.ErrorLogger - An error occured instantiating job to be executed. job= 'immediateEmailsGroup.DEFAULT.jobFor_1000new1'
org.quartz.SchedulerException: Problem instantiating class 'com.cambridgedata.notifications.EMailJob' - [See nested exception: java.lang.AbstractMethodError: org.springframework.scheduling.quartz.SpringBeanJobFactory.newJob(Lorg/quartz/spi/TriggerFiredBundle;Lorg/quartz/Scheduler;)Lorg/quartz/Job;]
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:141)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:381)
Caused by: java.lang.AbstractMethodError: org.springframework.scheduling.quartz.SpringBeanJobFactory.newJob(Lorg/quartz/spi/TriggerFiredBundle;Lorg/quartz/Scheduler;)Lorg/quartz/Job;
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:134)
thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
一种简单的方法是使用 @Component 注释来注释 Quartz 作业,然后 Spring 将为您完成所有 DI 魔法,因为它现在被识别为 Spring bean。我必须对
AspectJ
方面执行类似的操作 - 在我使用 Spring@Component
构造型对它进行注释之前,它不是一个 Spring bean。A simple way to do it would be to just annotate the Quartz Jobs with
@Component
annotation, and then Spring will do all the DI magic for you, as it is now recognized as a Spring bean. I had to do something similar for anAspectJ
aspect - it was not a Spring bean until I annotated it with the Spring@Component
stereotype.上面的解决方案很好,但就我而言,注射不起作用。我需要使用 autowireBeanProperties 来代替,可能是由于我的上下文配置方式:
The solution above is great but in my case the injection was not working. I needed to use autowireBeanProperties instead, probably due to the way my context is configured:
这是正确答案 http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030。并且适用于大多数人。但是,如果您的 web.xml 不知道所有 applicationContext.xml 文件,quartz 作业将无法调用这些 bean。我必须做一个额外的层来注入额外的 applicationContext 文件
。您可以添加您希望石英了解的任意数量的上下文文件。
This is the right answer http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030. and will work for most of the folks. But if your web.xml does is not aware of all applicationContext.xml files, quartz job will not be able to invoke those beans. I had to do an extra layer to inject additional applicationContext files
You can add any number of context files you want your quartz to be aware of.
只需从 QuartzJobBean 扩展您的工作即可
Simply extend your job from
QuartzJobBean
确保您的
依赖项是从而
不是从
它希望我使用
而不是因此
无法自动装配作业实例。
Make sure your
dependency is pulled from
and NOT from
It wanted me to use
instead of
so was failing to autowire job instance.
当您已经在项目中使用真正的 AspectJ 时,您可以使用
@Configurable
注释作业 bean 类。然后Spring会注入到这个类中,即使它是通过new
构造的When you already use real AspectJ in your project, then you could annotate the job bean class with
@Configurable
. Then Spring will inject into this class, even if it is constructed vianew
我遇到了类似的问题,并用以下方法解决了这个问题:
在上面的代码中,我将 dao.DAOFramework bean 注入到 JobA bean 中,在 ExecuteInternal 方法中,您可以获得注入的 bean,如下所示:
我希望它有帮助!谢谢。
I faced the similiar problem and came out from it with following approach:
In above code I inject dao.DAOFramework bean into JobA bean and in inside ExecuteInternal method you can get injected bean like:
I hope it helps! Thank you.
当我想调用事务方法时,上述所有这些解决方案对于 Spring 5、Hibernate 5 和 Quartz 2.2.3 都不起作用!
因此,我实现了这个解决方案,它自动启动调度程序并触发作业。我在 dzone 找到了很多这样的代码。因为我不需要动态创建触发器和作业,所以我希望通过 Spring 配置预先定义静态触发器,并且仅将作业公开为 Spring 组件。
我的基本配置如下所示
如您所见,您有调度程序和一个通过 cron 表达式定义的简单测试触发器。显然,您可以选择您喜欢的任何调度表达式。然后,您需要 AutowiringSpringBeanJobFactory,如下所示。
在这里,您将正常的应用程序上下文和作业连接在一起。这是一个重要的差距,因为通常 Quartz 启动它的工作线程,这些线程与您的应用程序上下文没有连接。这就是您无法执行事务方法的原因。最后缺少的就是工作。它可能看起来像这样,
这不是一个完美的解决方案,因为您有一个额外的类仅用于调用您的服务方法。但无论如何它还是有效的。
All those solutions above doesn't work for me with Spring 5 and Hibernate 5 and Quartz 2.2.3 when I want to call transactional methods!
I therefore implemented this solution which automatically starts the scheduler and triggers the jobs. I found a lot of that code at dzone. Because I don't need to create triggers and jobs dynamically I wanted the static triggers to be pre defined via Spring Configuration and only the jobs to be exposed as Spring Components.
My basic configuration look like this
As you can see, you have the scheduler and a simple test trigger which is defined via a cron expression. You can obviously choose whatever scheduling expression you like. You then need the AutowiringSpringBeanJobFactory which goes like this
In here you wire your normal application context and your job together. This is the important gap because normally Quartz starts it's worker threads which have no connection to your application context. That is the reason why you can't execute Transactional mehtods. The last thing missing is a job. It can look like that
It's not a perfect solution because you an extra class only for calling your service method. But nevertheless it works.
Jdbc jobstore
如果您使用 jdbc jobstore,Quartz 使用不同的类加载器。这阻止了自动装配的所有解决方法,因为来自 spring 的对象在quartz 端不兼容,因为它们源自不同的类加载器。
要解决这个问题,必须在quartz属性文件中设置默认的类加载器,如下所示:
作为参考:
https://github.com/quartz-scheduler/quartz/issues/221
Jdbc jobstore
If you are using jdbc jobstore Quartz uses a different classloader. That prevents all the workarounds for autowiring, since objects from spring will not be compatible at quartz side, because they originited from a different class loader.
To fix that, the default classloader has to be set in the quartz properties file like this:
As reference:
https://github.com/quartz-scheduler/quartz/issues/221
您可以使用此
SpringBeanJobFactory
使用 spring 自动装配石英对象:然后,将其附加到您的
SchedulerBean
(在本例中,使用 Java-config):为我工作,使用spring-3.2.1 和quartz-2.1.6。
请在此处查看完整要点。
我在此找到了解决方案博客文章
You can use this
SpringBeanJobFactory
to automatically autowire quartz objects using spring:Then, attach it to your
SchedulerBean
(in this case, with Java-config):Working for me, using spring-3.2.1 and quartz-2.1.6.
Check out the complete gist here.
I found the solution in this blog post
我只是将 SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); 作为我的
Job.execute(JobExecutionContext context)
方法的第一行。I just put
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
as first line of myJob.execute(JobExecutionContext context)
method.同样的问题已在 LINK:
我可以从 Spring 论坛上的帖子中找到其他选项,您可以通过 SchedulerFactoryBean 传递对 Spring 应用程序上下文的引用。就像下面的例子一样:
然后在作业类中使用下面的代码,您可以获得 applicationContext 并获得您想要的任何 bean。
希望有帮助。
您可以从 Mark Mclaren 获取更多信息博客
Same problem has been resolved in LINK:
I could found other option from post on the Spring forum that you can pass a reference to the Spring application context via the SchedulerFactoryBean. Like the example shown below:
Then using below code in your job class you can get the applicationContext and get whatever bean you want.
Hope it helps.
You can get more information from Mark Mclaren'sBlog
你对 Spring 与 Quartz 实例化类的假设是正确的。然而,Spring 提供了一些类,让您可以在 Quartz 中进行一些原始的依赖注入。查看 SchedulerFactoryBean.setJobFactory() 以及 SpringBeanJobFactory。本质上,通过使用 SpringBeanJobFactory,您可以对所有 Job 属性启用依赖注入,但仅限于 Quartz 调度程序上下文 或 作业数据图。我不知道它支持哪些 DI 样式(构造函数、注释、setter...),但我知道它支持 setter 注入。
You're right in your assumption about Spring vs. Quartz instantiating the class. However, Spring provides some classes that let you do some primitive dependency injection in Quartz. Check out SchedulerFactoryBean.setJobFactory() along with the SpringBeanJobFactory. Essentially, by using the SpringBeanJobFactory, you enable dependency injection on all Job properties, but only for values that are in the Quartz scheduler context or the job data map. I don't know what all DI styles it supports (constructor, annotation, setter...) but I do know it supports setter injection.
对于所有将来尝试此操作的人。
org.springframework.scheduling.quartz.JobDetailBean
提供对象的映射,这些对象可能是 spring beans。
定义类似
,然后在内部
调用
myBean = (myBean) context.getMergedJobDataMap().get("myBean");
你们都准备好了。
我知道,它看起来很难看,但作为一种解决方法,它是有效的
for all who will try this in the future.
org.springframework.scheduling.quartz.JobDetailBean
supplies map of objects and those objects may be spring beans.
define smth like
and then, inside
call
myBean = (myBean) context.getMergedJobDataMap().get("myBean");
and you all set.
I know, it looks ugly, but as a workaround it works
谢谢,里彭!
经过多次努力,我终于也得到了这个工作,我的解决方案非常接近你的建议!关键是让我自己的 Job 扩展 QuartzJobBean,并使用 SchedulerContextAsMap。
我确实在没有指定 applicationContextSchedulerContextKey 属性的情况下逃脱了 - 它对我来说没有它就可以工作。
为了其他人的利益,这里是对我有用的最终配置:
请注意,“mailService”bean 是我自己的服务 bean,由 Spring 管理。我能够在我的作业中访问它,如下所示:
并且此配置也允许我通过使用工厂获取触发器和 JobDetails 并以编程方式设置所需参数来动态调度作业:
再次感谢所有提供帮助的人,
Marina
Thanks, Rippon!
I have finally got this working too, after many struggles, and my solution is very close to what you suggested! The key was to make my own Job to extend QuartzJobBean, and to use the schedulerContextAsMap.
I did get away without specifying the applicationContextSchedulerContextKey property - it worked without it for me.
For the benefit of others, here is the final configuration that has worked for me:
Notice that the 'mailService" bean is my own service bean, managed by Spring. I was able to access it in my Job as following:
And this configuration also allowed me to dynamically scheduler jobs, by using factories to get Triggers and JobDetails and setting required parameters on them programmatically:
Thanks a lot again to everybody who helped,
Marina
一个简单的解决方案是在作业数据映射中设置 spring bean,然后在作业类中检索该 bean,例如
`
A simple solution is to set the spring bean in the Job Data Map and then retrieve the bean in the job class, for instance
`
以下是带有 @Component 的代码的样子:
安排作业的主类:
除了 @Component 注释之外,EmailJob 与我的第一篇文章中的相同:
Spring 的配置文件具有:
感谢所有帮助!
码头
Here is what the code looks like with @Component:
Main class that schedules the job:
The EmailJob is the same as in my first posting except for the @Component annotation:
And the Spring's configuration file has:
Thanks for all the help!
Marina
这是一篇相当老的帖子,但仍然有用。提出这两个的所有解决方案都有一些不适合所有的条件:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
这假设或要求它是一个基于 spring - web 的项目AutowiringSpringBeanJobFactory
前面的答案中提到的基于方法非常有帮助,但答案特定于那些不使用纯香草石英 api 而是使用 Spring 的石英包装器来执行以下操作的人 相同的。如果您想继续使用纯 Quartz 实现来进行调度(带有 Spring 自动装配功能的 Quartz),我可以按如下方式执行:
我希望尽可能以 Quartz 方式进行操作,因此事实证明,这个小窍门很有帮助。
quartzScheduler.setJobFactory(autowiringSpringBeanJobFactory); 为我们提供了一个自动装配的作业实例。由于 AutowiringSpringBeanJobFactory 隐式实现了 JobFactory,我们现在启用了自动连接解决方案。希望这有帮助!
This is a quite an old post which is still useful. All the solutions that proposes these two had little condition that not suite all:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
This assumes or requires it to be a spring - web based projectAutowiringSpringBeanJobFactory
based approach mentioned in previous answer is very helpful, but the answer is specific to those who don't use pure vanilla quartz api but rather Spring's wrapper for the quartz to do the same.If you want to remain with pure Quartz implementation for scheduling(Quartz with Autowiring capabilities with Spring), I was able to do it as follows:
I was looking to do it quartz way as much as possible and thus little hack proves helpful.
quartzScheduler.setJobFactory(autowiringSpringBeanJobFactory);
gives us an autowired job instance. SinceAutowiringSpringBeanJobFactory
implicitly implements aJobFactory
, we now enabled an auto-wireable solution. Hope this helps!Hary https://stackoverflow.com/a/37797575/4252764 的解决方案效果非常好。它更简单,不需要那么多特殊的工厂bean,并且支持多个触发器和作业。
只需补充一点,Quartz 作业可以变得通用,而特定作业可以作为常规 Spring bean 实现。
A solution from Hary https://stackoverflow.com/a/37797575/4252764 works very well. It's simpler, doesn't need so many special factory beans, and support multiple triggers and jobs.
Would just add that Quartz job can be made to be generic, with specific jobs implemented as regular Spring beans.