Мне удалось настроить и запланировать задание Quartz с помощью постоянного хранилища JobStoreTX в Spring. Я не использую задания Spring Quartz, потому что мне нужно планировать их динамически, во время выполнения, и все примеры интеграции Spring с Quartz, которые я обнаружил, жестко кодировали shcedules в файлах конфигурации Spring ... В любом случае, вот как Я планирую работу:
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 - это простая задача по отправке электронной почты с использованием класса JavaMailSenderImpl Spring.
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");
}
Проблема в том, что мне нужно получить ссылку на экземпляр этого класса (JavaMailSenderImpl) в моем классе EMailJob. Когда я пытаюсь ввести это так:
@Autowired
private JavaMailSenderImpl mailSenderImpl;
он не вводится - ссылка NULL. Я предполагаю, что это происходит потому, что не Spring создает экземпляр класса EMailJob, а Quartz, а Quartz ничего не знает о внедрении зависимостей ...
Итак, есть ли способ заставить эту инъекцию произойти?
Благодарность!
Обновление 1: @Aaron: вот соответствующая часть трассировки стека из запуска, которая показывает, что экземпляр 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. Похоже, что сам планировщик создан нормально, но ошибка возникает, когда он пытается создать экземпляр моего 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)
Благодарность!
источник
Я просто поставил
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
первую строчку своегоJob.execute(JobExecutionContext context)
метода.источник
Та же проблема была решена в LINK :
Я мог найти другой вариант из сообщения на форуме Spring, в котором вы можете передать ссылку на контекст приложения Spring через SchedulerFactoryBean. Как в примере, показанном ниже:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <propertyy name="triggers"> <list> <ref bean="simpleTrigger"/> </list> </property> <property name="applicationContextSchedulerContextKey"> <value>applicationContext</value> </property>
Затем, используя приведенный ниже код в своем классе работы, вы можете получить applicationContext и получить любой компонент, который хотите.
appCtx = (ApplicationContext)context.getScheduler().getContext().get("applicationContextSchedulerContextKey");
Надеюсь, это поможет. Вы можете получить больше информации в блоге Марка Макларена.
источник
Вы правы в своем предположении о Spring vs. Quartz, создающем экземпляр класса. Однако Spring предоставляет несколько классов, которые позволяют выполнять простейшие инъекции зависимостей в Quartz. Ознакомьтесь с SchedulerFactoryBean.setJobFactory () вместе с SpringBeanJobFactory . По сути, с помощью SpringBeanJobFactory вы включаете внедрение зависимостей для всех свойств задания, но только для значений, которые находятся в контексте планировщика Quartz или на карте данных задания . Я не знаю, какие все стили DI он поддерживает (конструктор, аннотация, установщик ...), но я знаю, что он поддерживает внедрение установщика.
источник
для всех, кто попробует это в будущем.
org.springframework.scheduling.quartz.JobDetailBean предоставляет карту объектов, и эти объекты могут быть Spring beans.
определять что-л. как
<bean name="myJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="my.cool.class.myCoolJob" /> <property name="jobDataAsMap"> <map> <entry key="myBean" value-ref="myBean" /> </map> </property> </bean>
а затем внутри
public void executeInternal(JobExecutionContext context)
звоните
myBean = (myBean) context.getMergedJobDataMap().get("myBean");
и все готово. Я знаю, это выглядит некрасиво, но как обходной путь работаетисточник
ApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(ContextLoaderListener .getCurrentWebApplicationContext().getServletContext()); Bean bean = (Bean) springContext.getBean("beanName"); bean.method();
источник
Спасибо, Риппон! Я наконец-то получил и эту работу после многих усилий, и мое решение очень близко к тому, что вы предложили! Ключевым моментом было создание моей собственной задачи по расширению QuartzJobBean и использование schedulerContextAsMap.
Я ушел, не указав свойство applicationContextSchedulerContextKey - у меня оно работало без него.
Для других вот последняя конфигурация, которая сработала для меня:
<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="configLocation" value="classpath:spring/quartz.properties"/> <property name="jobFactory"> <bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory" /> </property> <property name="schedulerContextAsMap"> <map> <entry key="mailService" value-ref="mailService" /> </map> </property> </bean> <bean id="jobTriggerFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName"> <idref local="jobTrigger" /> </property> </bean> <bean id="jobTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean" scope="prototype"> <property name="group" value="myJobs" /> <property name="description" value="myDescription" /> <property name="repeatCount" value="0" /> </bean> <bean id="jobDetailFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName"> <idref local="jobDetail" /> </property> </bean> <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean" scope="prototype"> <property name="jobClass" value="com.cambridgedata.notifications.EMailJob" /> <property name="volatility" value="false" /> <property name="durability" value="false" /> <property name="requestsRecovery" value="true" /> </bean> <bean id="notificationScheduler" class="com.cambridgedata.notifications.NotificationScheduler"> <constructor-arg ref="quartzScheduler" /> <constructor-arg ref="jobDetailFactory" /> <constructor-arg ref="jobTriggerFactory" /> </bean>
Обратите внимание, что bean-компонент mailService - это мой собственный служебный bean-компонент, управляемый Spring. Я смог получить к нему доступ в моем Job следующим образом:
public void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("EMailJob started ..."); .... SchedulerContext schedulerContext = null; try { schedulerContext = context.getScheduler().getContext(); } catch (SchedulerException e1) { e1.printStackTrace(); } MailService mailService = (MailService)schedulerContext.get("mailService"); ....
И эта конфигурация также позволила мне динамически планировать задания, используя фабрики для получения триггеров и JobDetails и программно задавая для них необходимые параметры:
public NotificationScheduler(final Scheduler scheduler, final ObjectFactory<JobDetail> jobDetailFactory, final ObjectFactory<SimpleTrigger> jobTriggerFactory) { this.scheduler = scheduler; this.jobDetailFactory = jobDetailFactory; this.jobTriggerFactory = jobTriggerFactory; ... // create a trigger SimpleTrigger trigger = jobTriggerFactory.getObject(); trigger.setRepeatInterval(0L); trigger.setStartTime(new Date()); // create job details JobDetail emailJob = jobDetailFactory.getObject(); emailJob.setName("new name"); emailJob.setGroup("immediateEmailsGroup"); ...
Еще раз спасибо всем, кто помогал,
Марина
источник
Простое решение - установить компонент Spring в карте данных задания, а затем получить этот компонент в классе задания, например
// the class sets the configures the MyJob class SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); Date startTime = DateBuilder.nextGivenSecondDate(null, 15); JobDetail job = newJob(MyJob.class).withIdentity("job1", "group1").build(); job.getJobDataMap().put("processDataDAO", processDataDAO);
`
// this is MyJob Class ProcessDataDAO processDataDAO = (ProcessDataDAO) jec.getMergedJobDataMap().get("processDataDAO");
источник
Вот как выглядит код с @Component:
Основной класс, который планирует работу:
public class NotificationScheduler { private SchedulerFactory sf; private Scheduler scheduler; @PostConstruct public void initNotificationScheduler() { try { sf = new StdSchedulerFactory("spring/quartz.properties"); scheduler = sf.getScheduler(); scheduler.start(); // test out sending a notification at startup, prepare some parameters... this.scheduleImmediateNotificationJob(messageParameters, recipients); try { // wait 20 seconds to show jobs logger.info("sleeping..."); Thread.sleep(40L * 1000L); logger.info("finished sleeping"); // executing... } catch (Exception ignore) { } } catch (SchedulerException e) { e.printStackTrace(); throw new RuntimeException("NotificationScheduler failed to retrieve a Scheduler instance: ", e); } } public void scheduleImmediateNotificationJob(){ try { JobKey jobKey = new JobKey("key"); Date fireTime = DateBuilder.futureDate(delayInSeconds, IntervalUnit.SECOND); JobDetail emailJob = JobBuilder.newJob(EMailJob.class) .withIdentity(jobKey.toString(), "immediateEmailsGroup") .build(); TriggerKey triggerKey = new TriggerKey("triggerKey"); SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger() .withIdentity(triggerKey.toString(), "immediateEmailsGroup") .startAt(fireTime) .build(); // schedule the job to run Date scheduleTime1 = scheduler.scheduleJob(emailJob, trigger); } catch (SchedulerException e) { logger.error("error scheduling job: " + e.getMessage(), e); e.printStackTrace(); } } @PreDestroy public void cleanup(){ sf = null; try { scheduler.shutdown(); } catch (SchedulerException e) { e.printStackTrace(); } }
EmailJob такой же, как и в моей первой публикации, за исключением аннотации @Component:
@Component public class EMailJob implements Job { @Autowired private JavaMailSenderImpl mailSenderImpl; ... }
И файл конфигурации Spring имеет:
... <context:property-placeholder location="classpath:spring/*.properties" /> <context:spring-configured/> <context:component-scan base-package="com.mybasepackage"> <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" /> </context:component-scan> <bean id="mailSenderImpl" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="${mail.host}"/> <property name="port" value="${mail.port}"/> ... </bean> <bean id="notificationScheduler" class="com.mybasepackage.notifications.NotificationScheduler"> </bean>
Спасибо за помощь!
Марина
источник
EmailJob
его инициализацию? Легкий способ проверить - добавить строку журнала в конструктор.Решение от Hary https://stackoverflow.com/a/37797575/4252764 работает очень хорошо. Это проще, не нужно столько специальных фабричных компонентов и поддерживает несколько триггеров и заданий. Я бы просто добавил, что задание Quartz может быть универсальным, а конкретные задания реализованы как обычные компоненты Spring.
public interface BeanJob { void executeBeanJob(); } public class GenericJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap dataMap = context.getMergedJobDataMap(); ((BeanJob)dataMap.get("beanJob")).executeBeanJob(); } } @Component public class RealJob implements BeanJob { private SomeService service; @Autowired public RealJob(SomeService service) { this.service = service; } @Override public void executeBeanJob() { //do do job with service } }
источник
Это довольно старый пост, который все еще полезен. Все решения, которые предлагают эти два, имеют небольшое условие, которое не подходит всем:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
Это предполагает или требует, чтобы это был весенний веб-проект.AutowiringSpringBeanJobFactory
подход на основе, упомянутый в предыдущем ответе, очень полезен, но ответ предназначен для тех, кто не использует чистый api ванильного кварца, а скорее обертку Spring для кварца, чтобы сделать то же самое.Если вы хотите остаться с чистой реализацией Quartz для планирования (Quartz с возможностями Autowiring с Spring), я смог сделать это следующим образом:
Я старался сделать это как можно больше кварцевым способом, и поэтому небольшой хак оказался полезным.
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory{ private AutowireCapableBeanFactory beanFactory; public AutowiringSpringBeanJobFactory(final ApplicationContext applicationContext){ beanFactory = applicationContext.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); beanFactory.initializeBean(job, job.getClass().getName()); return job; } } @Configuration public class SchedulerConfig { @Autowired private ApplicationContext applicationContext; @Bean public AutowiringSpringBeanJobFactory getAutowiringSpringBeanJobFactory(){ return new AutowiringSpringBeanJobFactory(applicationContext); } } private void initializeAndStartScheduler(final Properties quartzProperties) throws SchedulerException { //schedulerFactory.initialize(quartzProperties); Scheduler quartzScheduler = schedulerFactory.getScheduler(); //Below one is the key here. Use the spring autowire capable job factory and inject here quartzScheduler.setJobFactory(autowiringSpringBeanJobFactory); quartzScheduler.start(); }
quartzScheduler.setJobFactory(autowiringSpringBeanJobFactory);
дает нам экземпляр задания с автоматическим подключением. ПосколькуAutowiringSpringBeanJobFactory
неявно реализует aJobFactory
, теперь мы включили решение с автоматическим подключением. Надеюсь это поможет!источник
Простой способ сделать это - просто аннотировать Quartz Jobs с помощью
@Component
аннотации, и тогда Spring сделает за вас всю магию DI, поскольку теперь он распознается как компонент Spring. Мне пришлось сделать что-то подобное дляAspectJ
аспекта - это не был компонент Spring, пока я не аннотировал его@Component
стереотипом Spring .источник
EmailJob
класс в пакете, который будет сканироваться Spring при запуске приложения? Тот факт, что вы аннотировали,@Component
но внедренный класс по-прежнему имеет значение null, означает, что он не сканируется - в противном случае DI при запуске приложения вызовет исключение.Приведенное выше решение отличное, но в моем случае инъекция не сработала. Вместо этого мне нужно было использовать autowireBeanProperties, вероятно, из-за того, как настроен мой контекст:
import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.quartz.SpringBeanJobFactory; public final class AutowiringSpringBeanJobFactory 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); beanFactory.autowireBeanProperties(job, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true); return job; } }
источник
Это правильный ответ http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030 . и будет работать для большинства людей. Но если ваш web.xml не знает обо всех файлах applicationContext.xml, задание quartz не сможет вызвать эти bean-компоненты. Мне пришлось сделать дополнительный слой для вставки дополнительных файлов applicationContext
public class MYSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { try { PathMatchingResourcePatternResolver pmrl = new PathMatchingResourcePatternResolver(context.getClassLoader()); Resource[] resources = new Resource[0]; GenericApplicationContext createdContext = null ; resources = pmrl.getResources( "classpath*:my-abc-integration-applicationContext.xml" ); for (Resource r : resources) { createdContext = new GenericApplicationContext(context); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(createdContext); int i = reader.loadBeanDefinitions(r); } createdContext.refresh();//important else you will get exceptions. beanFactory = createdContext.getAutowireCapableBeanFactory(); } catch (IOException e) { e.printStackTrace(); } } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } }
Вы можете добавить любое количество файлов контекста, о которых должен знать кварц.
источник
Убедитесь, что ваш
AutowiringSpringBeanJobFactory extends SpringBeanJobFactory
зависимость извлекается из
"org.springframework:spring-context-support:4..."
а НЕ из
"org.springframework:spring-support:2..."
Он хотел, чтобы я использовал
@Override public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler)
вместо того
@Override protected Object createJobInstance(final TriggerFiredBundle bundle)
поэтому не удалось выполнить автоматическое подключение экземпляра задания.
источник
Если вы уже используете настоящий AspectJ в своем проекте, вы можете аннотировать класс компонента задания с помощью
@Configurable
. Затем Spring будет внедряться в этот класс, даже если он построен с помощьюnew
источник
Я столкнулся с похожей проблемой и решил ее следующим образом:
<!-- Quartz Job --> <bean name="JobA" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <!-- <constructor-arg ref="dao.DAOFramework" /> --> <property name="jobDataAsMap"> <map> <entry key="daoBean" value-ref="dao.DAOFramework" /> </map> </property> <property name="jobClass" value="com.stratasync.jobs.JobA" /> <property name="durability" value="true"/> </bean>
В приведенном выше коде я ввожу bean-компонент dao.DAOFramework в bean-компонент JobA, а внутри метода ExecuteInternal вы можете получить внедренный bean-компонент, например:
daoFramework = (DAOFramework)context.getMergedJobDataMap().get("daoBean");
Я надеюсь, что это помогает! Спасибо.
источник
Все вышеперечисленные решения не работают в Spring 5, Hibernate 5 и Quartz 2.2.3, когда я хочу вызывать транзакционные методы!
Поэтому я реализовал это решение, которое автоматически запускает планировщик и запускает задания. Я нашел много такого кода в dzone . Поскольку мне не нужно динамически создавать триггеры и задания, я хотел, чтобы статические триггеры были предварительно определены через конфигурацию Spring и только задания были представлены как компоненты Spring.
Моя базовая конфигурация выглядит так
@Configuration public class QuartzConfiguration { @Autowired ApplicationContext applicationContext; @Bean public SchedulerFactoryBean scheduler(@Autowired JobFactory jobFactory) throws IOException { SchedulerFactoryBean sfb = new SchedulerFactoryBean(); sfb.setOverwriteExistingJobs(true); sfb.setAutoStartup(true); sfb.setJobFactory(jobFactory); Trigger[] triggers = new Trigger[] { cronTriggerTest().getObject() }; sfb.setTriggers(triggers); return sfb; } @Bean public JobFactory cronJobFactory() { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } @Bean public CronTriggerFactoryBean cronTriggerTest() { CronTriggerFactoryBean tfb = new CronTriggerFactoryBean(); tfb.setCronExpression("0 * * ? * * *"); JobDetail jobDetail = JobBuilder.newJob(CronTest.class) .withIdentity("Testjob") .build() ; tfb.setJobDetail(jobDetail); return tfb; } }
Как видите, у вас есть планировщик и простой тестовый триггер, который определяется через выражение cron. Очевидно, вы можете выбрать любое выражение планирования, которое вам нравится. Затем вам понадобится AutowiringSpringBeanJobFactory, который выглядит следующим образом
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { @Autowired private ApplicationContext applicationContext; private SchedulerContext schedulerContext; @Override public void setApplicationContext(final ApplicationContext context) { this.applicationContext = context; } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { Job job = applicationContext.getBean(bundle.getJobDetail().getJobClass()); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap()); pvs.addPropertyValues(bundle.getTrigger().getJobDataMap()); if (this.schedulerContext != null) { pvs.addPropertyValues(this.schedulerContext); } bw.setPropertyValues(pvs, true); return job; } public void setSchedulerContext(SchedulerContext schedulerContext) { this.schedulerContext = schedulerContext; super.setSchedulerContext(schedulerContext); } }
Здесь вы объединяете свой обычный контекст приложения и свою работу. Это важный пробел, потому что обычно Quartz запускает свои рабочие потоки, которые не связаны с контекстом вашего приложения. Это причина, по которой вы не можете выполнять транзакционные методы. Последнее, чего не хватает, - это работы. Это может выглядеть так
@Component public class CronTest implements Job { @Autowired private MyService s; public CronTest() { } @Override public void execute(JobExecutionContext context) throws JobExecutionException { s.execute(); } }
Это не идеальное решение, потому что вы дополнительный класс только для вызова вашего метода обслуживания. Но тем не менее работает.
источник
Jdbc jobstore
Если вы используете jdbc jobstore, Quartz использует другой загрузчик классов. Это предотвращает все обходные пути для автоматического подключения, поскольку объекты из Spring не будут совместимы на стороне кварца, потому что они происходят из другого загрузчика классов.
Чтобы исправить это, загрузчик классов по умолчанию должен быть установлен в файле свойств кварца следующим образом:
org.quartz.scheduler.classLoadHelper.class=org.quartz.simpl.ThreadContextClassLoadHelper
Как ссылка: https://github.com/quartz-scheduler/quartz/issues/221
источник
Просто продлите свою работу с
QuartzJobBean
public class MyJob extends QuartzJobBean { @Autowired private SomeBean someBean; @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("Some bean is " + someBean.toString()); } }
источник