впрысните ссылку фасоли в работу кварца весной?
мне удалось настроить и запланировать задание Quartz с помощью jobstoretx persistent store весной. Я не использую задания 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;
он не вводится-ссылка равна нулю. Я предполагаю, что это происходит, потому что не Spring создает экземпляр класса EMailJob, а Quartz, а Quartz ничего не знает о инъекции зависимостей...
Итак, есть ли способ заставить эту инъекцию случилось?
спасибо!
обновление 1: @Арон: вот соответствующая часть stacktrace от запуска, которая показывает, что 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>
и я изменил свой основной класс, чтобы получить планировщик с этой фабрики, а не Quartz':
@PostConstruct
public void initNotificationScheduler() {
try {
//sf = new StdSchedulerFactory("spring/quartz.properties");
//scheduler = sf.getScheduler();
scheduler = schedulerFactoryBean.getScheduler();
scheduler.start();
....
но когда я запускаю приложение - возникают ошибки, см. ниже. Вот трассировка стека из весеннего запуска . Похоже, что сам планировщик создан нормально, но ошибка возникает, когда он пытается создать экземпляр 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)
спасибо!
19 ответов:
просто обертка решения, предоставленного jelies плюс несколько других вещей, чтобы сделать интеграцию Spring+Quartz проще: http://codrspace.com/Khovansa/spring-quartz-with-a-database/
вы можете использовать этот
SpringBeanJobFactory
для автоматического автоматического подключения кварцевых объектов с помощью spring: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); return job; } }
затем прикрепите его к своему
SchedulerBean
(в данном случае с Java-config):@Bean public SchedulerFactoryBean quartzScheduler() { SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean(); ... AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); quartzScheduler.setJobFactory(jobFactory); ... return quartzScheduler; }
работая для меня, используя весну-3.2.1 и кварц-2.1.6.
Проверьте полную суть здесь.
Я нашел решение в этой блоге
Я просто ставлю
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
как первая строка моегоJob.execute(JobExecutionContext context)
метод.
такая же проблема была решена в ссылке:
я мог бы найти другой вариант из сообщения на весеннем форуме, что вы можете передать ссылку на контекст приложения 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");
надеюсь, что это помогает. Вы можете получить дополнительную информацию от Марк Макларен'sBlog
вы правы в своем предположении о весне и кварцевые инстанцирования класса. Однако Spring предоставляет некоторые классы, которые позволяют выполнять некоторые примитивные инъекции зависимостей в Quartz. Проверьте SchedulerFactoryBean.setJobFactory () вместе с SpringBeanJobFactory. По существу, с помощью SpringBeanJobFactory вы включаете инъекцию зависимостей для всех свойств задания, но только для значений, которые находятся в Quartz контексте планировщика или карта данных работы. Я не знаю, какие все стили DI он поддерживает (конструктор, аннотация, сеттер...) но я знаю, что он поддерживает инъекции сеттера.
для всех, кто будет в будущем.
орг.springframework.планирование.кварцевый.JobDetailBean поставляет карту объектов, и эти объекты могут быть весенними бобами.
определить что-то вроде
<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>
обратите внимание, что компонент" mailService " является моим собственным служебным компонентом, управляемым Spring. Я смог получить доступ к нему в моей работе следующим образом:
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"); ...
еще раз большое спасибо всем, кто помогал,
Марина
вот как выглядит код с @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(); } }
задание электронной почты такое же, как и в моей первой публикации, за исключением аннотации @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>
Спасибо за помощь!
Марина
простое решение состоит в том, чтобы установить spring bean в карте данных задания, а затем получить bean в классе задания, например
// 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");
решение от Hary https://stackoverflow.com/a/37797575/4252764 работает очень хорошо. Это проще, не нужно так много специальных фабричных бобов и поддерживает несколько триггеров и заданий. Просто добавлю, что задание Quartz может быть сделано общим, с конкретными заданиями, реализованными как обычные весенние бобы.
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 } }
простой способ сделать это было бы просто аннотировать кварцевые задания с
@Component
аннотация, а затем Spring будет делать всю магию DI для вас, так как теперь она распознается как Spring bean. Я должен был сделать что-то подобное дляAspectJ
аспект-это был не весенний Боб, пока я не аннотировал его весной@Component
стереотип.
убедитесь, что ваш
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)
так не удалось autowire экземпляр задания.
когда вы уже используете real AspectJ в своем проекте, вы можете аннотировать класс job bean с помощью
@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>
в приведенном выше коде я ввожу dao.DAOFramework зернах в зернах Работаа и внутри метода ExecuteInternal вы можете получить вводили в зернах, таких как:
daoFramework = (DAOFramework)context.getMergedJobDataMap().get("daoBean");
Я надеюсь, что это помогает! Спасибо.
решение выше отлично, но в моем случае инъекция не работала. Вместо этого мне нужно было использовать 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и будет работать для большинства людей. Но если ваша паутина.xml does не знает обо всех applicationContext.xml-файлы, задание quartz не смогут вызвать эти бобы. Мне пришлось сделать дополнительный слой, чтобы ввести дополнительные файлы 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; } }
вы можете добавить любое количество файлов контекста, которые вы хотите, чтобы ваш кварц был знать.
это довольно старый пост, который еще пригодится. Все решения, которые предлагает эти два было мало условие, что не люкс все:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
это предполагает или требует, чтобы это был проект на основе spring-webAutowiringSpringBeanJobFactory
основанный подход, упомянутый в предыдущем ответе, очень полезен, но ответ специфичен для тех, кто не использует чистый ванильный кварцевый api, а скорее обертку весны для кварца, чтобы сделать тот же.если вы хотите остаться с чистой кварцевой реализацией для планирования (кварц с возможностями Автопроводки с пружиной), я смог сделать это следующим образом:
Я искал, чтобы сделать это кварцевый путь как можно больше и, таким образом, мало Хак оказывается полезным.
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);
дает нам экземпляр задания autowired. Так какAutowiringSpringBeanJobFactory
неявно реализует aJobFactory
, теперь мы включили автоматическое проводное решение. Надеюсь, это поможет!
все эти решения выше не работают для меня с 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); } }
здесь вы проводите свой обычный контекст приложения и свою работу вместе. Это важный зазор, потому что обычно кварц начинает это рабочие потоки, которые не имеют никакого отношения к контексту приложения. Это причина, почему вы не можете выполнить транзакций mehtods. Последнее, чего не хватает-это работа. Это может выглядеть так
@Component public class CronTest implements Job { @Autowired private MyService s; public CronTest() { } @Override public void execute(JobExecutionContext context) throws JobExecutionException { s.execute(); } }
Это не идеальное решение, потому что вы экстра-класса только для вызова метода сервиса. Но тем не менее это работает.