Baharda bir Quartz işine fasulye referansı mı enjekte edersiniz?


96

Spring'deki JobStoreTX kalıcı mağazasını kullanarak bir Quartz işi yapılandırmayı ve planlamayı başardım. Spring'in Quartz işlerini kullanmıyorum çünkü onları çalışma zamanında dinamik olarak planlamam gerekiyor ve bulduğum tüm Spring ile Quartz entegrasyon örnekleri, Spring yapılandırma dosyalarındaki shcedules'i sabit kodluyordu ... Neyse, işte nasıl İşi planlarım:

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'in JavaMailSenderImpl sınıfını kullanarak e-posta gönderen basit bir iştir.

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");

}

Sorun şu ki, EMailJob sınıfımda bu sınıfın bir örneğine (JavaMailSenderImpl) bir başvuru almam gerekiyor. Bunu böyle enjekte etmeye çalıştığımda:

@Autowired
private JavaMailSenderImpl mailSenderImpl;

enjekte edilmez - referans NULL. Bunun olduğunu varsayıyorum çünkü EMailJob sınıfını örnekleyen Spring değil, Quartz ve Quartz bağımlılık enjeksiyonu hakkında hiçbir şey bilmiyor ...

Öyleyse, bu enjeksiyonu yapmaya zorlamanın bir yolu var mı?

Teşekkürler!

Güncelleme 1: @Aaron: EMailJob'un iki kez başlatıldığını gösteren, başlangıçtaki stacktrace'in ilgili bir bölümünü burada bulabilirsiniz:

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 ...

Teşekkürler!

Güncelleme # 2: @Ryan:

SpringBeanJobFactory'yi aşağıdaki gibi kullanmayı denedim:

    <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>

Ve bu fabrikadan Scheduler'ı Quartz 'yerine almak için ana sınıfımı değiştirdim:

    @PostConstruct
public void initNotificationScheduler() {
    try {
        //sf = new StdSchedulerFactory("spring/quartz.properties");
        //scheduler = sf.getScheduler();

        scheduler = schedulerFactoryBean.getScheduler();
        scheduler.start();
            ....

Ancak uygulamayı çalıştırdığımda - hataları alın, aşağıya bakın. İşte Bahar başlangıcından elde edilen yığın izleme. Zamanlayıcının kendisi iyi oluşturulmuş gibi görünüyor, ancak EMailJob'umu başlatmaya çalışırken hata geliyor:

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)

Teşekkürler!

Yanıtlar:


131

Bunu, SpringBeanJobFactoryyayı kullanarak kuvars nesnelerini otomatik olarak otomatik olarak bağlamak için kullanabilirsiniz :

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;
    }
}

Ardından, bunu SchedulerBean(bu durumda Java-config ile) hesabınıza ekleyin :

@Bean
public SchedulerFactoryBean quartzScheduler() {
    SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();

    ...

    AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    quartzScheduler.setJobFactory(jobFactory);

    ...

    return quartzScheduler;
}

Spring-3.2.1 ve quartz-2.1.6 kullanarak benim için çalışıyor.

Özüne buradan göz atın .

Çözümü bu blog gönderisinde buldum


13
Bunun için bir ödül kazanmalısın, bu harika!
Nathan Feger

2
Çözüm gerçekten harika! Blog gönderisinin yazarına tüm krediler :)
jelies

3
Teşekkürler - bu günlerimi kurtardı! Spring neden bu OOB'yi sağlamadı. İlkbaharda Quartz kullanmak için en temel gereksinim budur.
HandyManDan

4
bu varsayılan uygulama olmalıdır :)
Diego Plentz

2
harika bir çözüm, ancak AutowireCapableBeanFactory beanFactory'nin neden "geçici" olarak işaretlendiğine dair bir fikri olan var mı? AutowiringSpringBeanJobFactory zaten Serileştirilecek görünmüyor, bu yüzden ne beanFactory hiç tefrika edilmesi gerekecektir
Marios

57

Yöntemimin SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);ilk satırı olarak koydum Job.execute(JobExecutionContext context).


7
Bu gerçek çözüm. Spring 3.2.4.RELEASE ve Quartz 2.2.0 ile test edilmiştir. ;)
aloplop85

3
@msangel - iyi, ikisi de işe yarayacak, ancak Quartz işinizde SpringBeanAutowiringSupport'u kullanmanın sorunu, iş örneğinin artık tüm IoC (dep enjeksiyon) fikrine aykırı olan Spring hakkında BİLİNMESİ gerektiğidir. Şimdi örneğin CDI kullanmanız gerekiyorsa, tek bir fabrika yerine tüm kuvars işlerinizin ayarlanması gerekecektir.
demaniak

2
Bu, bir web uygulaması bağlamı aradığı için bir birim testinde benim için işe yaramadı. @Jelies'den gelen cevabı kullanmak zorunda kaldım
Wim Deblauwe

5
Bu çözüm 4.1.4 bahar ve Quartz 2.2.1 ile çalışmıyor
skywalker

1
Ben de bu problemi yaşadım ve bu çözümü denedim. Çalışıyor ANCAK önceden oluşturulmuş olanı (varsayılan tekli) kullanmak yerine yeni bir örnek oluşturur. Her neyse, scheduler.getContext (). Put ("nesneAdı", nesne);
Krzysztof Cieśliński

13

Aynı sorun LINK'de çözüldü :

Spring forumundaki gönderiden, SchedulerFactoryBean aracılığıyla Spring uygulama içeriğine bir referans iletebileceğiniz başka bir seçenek buldum. Aşağıda gösterilen örnek gibi:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyy name="triggers">
    <list>
        <ref bean="simpleTrigger"/>
            </list>
    </property>
    <property name="applicationContextSchedulerContextKey">
        <value>applicationContext</value>
</property>

Ardından, meslek sınıfınızda aşağıdaki kodu kullanarak applicationContext'i alabilir ve istediğiniz fasulyeyi alabilirsiniz.

appCtx = (ApplicationContext)context.getScheduler().getContext().get("applicationContextSchedulerContextKey");

Umarım yardımcı olur. Mark Mclaren'sBlog'dan daha fazla bilgi alabilirsiniz.


1
Teşekkürler @Rippon! Çok fazla denedikten ve başarısız olduktan sonra, önerdiğiniz çok benzer bir yaklaşım kullandım: applicationContextSchedulerContextKey özelliğini ve uygulama bağlamını kullanmadım, ancak 'code' <property name = "schedulerContextAsMap"> <map> <entry key = "mailService" value-ref = "mailService" /> </map> </property>
Marina

8

Sınıfı örnekleyen Spring vs Quartz hakkındaki varsayımınızda haklısınız. Ancak Spring, Quartz'da bazı ilkel bağımlılık enjeksiyonu yapmanıza izin veren bazı sınıflar sağlar. Check out SchedulerFactoryBean.setJobFactory () ile birlikte SpringBeanJobFactory . Esasen, SpringBeanJobFactory'yi kullanarak, tüm İş özelliklerinde bağımlılık enjeksiyonunu etkinleştirirsiniz, ancak yalnızca Quartz zamanlayıcı bağlamında veya iş veri haritasındaki değerler için . Desteklediği tüm DI stillerini bilmiyorum (yapıcı, açıklama, ayarlayıcı ...) ama ayarlayıcı enjeksiyonu desteklediğini biliyorum.


Merhaba Ryan, önerileriniz için teşekkürler. Bağımlılık enjeksiyonunu etkinleştirmek için önceden yapılandırılmış Tetikleyiciler ve QuartzJobBean'ı genişleten işler ile birlikte SpringBeanFactoryJob'u kullanmam gerektiğini mi söylüyorsunuz? Bu yaklaşımla ilgili sorun, tetikleyicilerin, çalışma zamanında dinamik zamanlamalarla tetikleyicileri tanımlayabilmem gereken Spring'in yapılandırma dosyalarında statik olarak tanımlanmasıdır ... Daha fazla ayrıntı için aşağıdaki bir sonraki cevabıma bakın - içinde yeterli alan yok yorum alanı ...
Marina

@ Marina: Hayır, işler böyle yürümüyor. SpringBeanJobFactory'yi kullanın ve istediğiniz şekilde yapın. Sadece çalışacak. Ayrıca, yalnızca sorunuz için bir güncelleme olan bir yanıt göndermeyin. Bunun yerine sorunuzu düzenleyin.
Ryan Stewart

üzgünüm, az önce yorumunuzu fark ettim! Önerdiğiniz gibi deneyeceğim ve sonuçları size bildireceğim. Yardımın için teşekkürler! Oh, cevap vermek yerine sorumu düzenlemeye çalışacağım ...
Marina

7

bunu gelecekte deneyecek olan herkes için.

org.springframework.scheduling.quartz.JobDetailBean nesnelerin haritasını sağlar ve bu nesneler bahar fasulyesi olabilir.

tanımlamak

<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>

ve sonra içeride

public void executeInternal(JobExecutionContext context)

arayın myBean = (myBean) context.getMergedJobDataMap().get("myBean"); ve hazırsınız. Biliyorum, çirkin görünüyor ama bir çözüm olarak işe yarıyor


IMHO Bu çözümün kuvars işlerine otomatik kablolama özelliğini eklemeye çalışmaktan daha temiz ve "doğal" olduğunu hissediyorum, bu yüzden bunun bir çalışma olduğunu düşünmüyorum.
reallynice

6
ApplicationContext springContext =

WebApplicationContextUtils.getWebApplicationContext(ContextLoaderListener .getCurrentWebApplicationContext().getServletContext());

Bean bean = (Bean) springContext.getBean("beanName");

bean.method();

4

Teşekkürler Rippon! Sonunda birçok mücadeleden sonra bunu da çalıştırdım ve benim çözümüm önerdiğinize çok yakın! Anahtar, QuartzJobBean'ı genişletmek için kendi İşimi yapmak ve schedulerContextAsMap'i kullanmaktı.

ApplicationContextSchedulerContextKey özelliğini belirtmeden kurtuldum - benim için onsuz çalıştı.

Başkalarının yararı için, işte benim için çalışan son yapılandırma:

    <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 "bean'in, Spring tarafından yönetilen kendi servis beanim olduğuna dikkat edin. İşimde şu şekilde erişebildim:

    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");
    ....

Ve bu yapılandırma ayrıca fabrikaları kullanarak Tetikleyicileri ve İş Ayrıntılarını alıp bunlarda gerekli parametreleri programlama yoluyla ayarlayarak işleri dinamik olarak planlamamı sağladı:

    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");
            ...

Yardımcı olan herkese tekrar çok teşekkürler,

yat Limanı


4

Basit bir çözüm, yaylı fasulyeyi İş Verileri Haritasına yerleştirmek ve ardından mesela iş sınıfındaki fasulyeyi almaktır.

// 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");

iş verilerinin blob olarak depolandığını düşünürsek (db kalıcılığı kullanılırken) bu, veri tabanı performans sorunlarına yol açabilir (Verilerin gerçekten çok büyük olduğunu hayal edin)
Sudip Bhandari

3

@Component ile kod şu şekilde görünür:

İşi planlayan ana sınıf:

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 ek açıklaması haricinde ilk gönderimdeki ile aynı:

@Component
public class EMailJob implements Job { 
  @Autowired
  private JavaMailSenderImpl mailSenderImpl;
... }

Spring'in yapılandırma dosyasında şunlar bulunur:

...
<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>

Tüm yardımlar için teşekkürler!

yat Limanı


Uygulamanız başladığında, EmailJobbaşlatıldığını görüyor musunuz? Kontrol etmenin kolay bir yolu, yapıcıya bir günlük satırı eklemektir.
58'de atrain

@Aaron: evet, var - ama az önce keşfettiğim gibi, iki kez başlatılıyor! Bir kez Spring çerçevesinin kendisi tarafından (ve bahse girerim bu örneğin içine posta hizmeti enjekte edilmiştir ...) ve daha sonra, Quartz'ın kendisi başlatıldıktan sonra - EMailJob, Quartz çerçevesi tarafından yeniden başlatılıyor - ve işte bu bu hizmet enjekte edilmiyor ... Ryan'ın önerdiği gibi sorumu düzenleyerek Spring'in başlangıcına ait bir yığın izini eklemeye çalışacağım ...
Marina

2

Hary https://stackoverflow.com/a/37797575/4252764'ten bir çözüm çok iyi çalışıyor. Daha basittir, çok fazla özel fabrika fasulyesine ihtiyaç duymaz ve birden çok tetikleyiciyi ve işi destekler. Sadece Quartz işinin normal Bahar fasulyesi olarak uygulanan belirli işler ile jenerik hale getirilebileceğini eklemek ister misiniz?

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
  }

}

Teşekkürler. Bunu ben de düşündüm. Ama benim durumumda, herhangi bir kuvars uygulamasını özetleyen bir kütüphane yazıyordum. Bu, herhangi bir nesneyi almak için 'anahtar' adını hatırlamak için gereklidir. Bunu saf kuvars yöntemiyle yapabildim ve sadece bir cevap olarak gönderdim. Pls düşüncelerinizi paylaşın!
Karthik R

2

Bu hala kullanışlı olan oldukça eski bir gönderi. Bu ikisini öneren tüm çözümlerin hiçbirine uymayan çok az koşulu vardı:

  • SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); Bu, web tabanlı bir proje olduğunu varsayar veya gerektirir
  • AutowiringSpringBeanJobFactory Önceki cevapta bahsedilen temelli yaklaşım çok yararlıdır, ancak cevap, saf vanilya kuvars api kullanmayanlara özeldir, bunun yerine aynı şeyi yapmak için kuvars için Spring'in sargısıdır.

Zamanlama için saf Quartz uygulamasında kalmak istiyorsanız (Yaylı Otomatik Kablolama özellikli Quartz), bunu şu şekilde yapabildim:

Bunu olabildiğince kuvars yöntemiyle yapmak istiyordum ve bu nedenle küçük bir hack yardımcı oldu.

 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);bize otomatik bağlantılı bir iş örneği verir. Yana AutowiringSpringBeanJobFactoryörtülü bir uygular JobFactory, şimdi bir otomatik montajına uygun hazırlanmış bir çözüm sağladı. Bu yardımcı olur umarım!


1

Bunu yapmanın basit bir yolu, Quartz İşlerine açıklama eklemek olabilir @Componentve ardından Bahar artık bir Bahar fasulyesi olarak tanındığından, sizin için tüm DI sihrini yapacak. Bir AspectJyön için benzer bir şey yapmak zorunda kaldım - Bahar @Componentklişesini ekleyene kadar Bahar fasulyesi değildi .


Teşekkürler Aaron, az önce denedim - ama maalesef aynı NPE oluyor - ve posta servisi iş çekirdeğine enjekte edilmiyor ...
Marina

Sizin mi EmailJobUygulamanın başlangıçta Bahar tarafından taranacak olan bir paket içinde sınıf? Ek açıklama eklemiş olmanız @Componentancak enjekte edilen sınıfın hala boş olması, bunun taranmadığını gösterir - aksi takdirde uygulama başlangıcındaki DI bir istisna atar.
atrain

Harun: evet, taranması gerekiyor - bunu yapması gereken <context: component-scan base-package = "com.mybasepackage"> bende var ... Bir sonraki cevabımda ana kodumun tam kodunu veriyorum sınıf, Bahar konfigürasyonuyla - her ihtimale karşı bariz bir şey tespit edilebilir ...
Marina

"@Autowired" ile işaretlenmiş iş alanları, İşi "@Component" ile işaretleseniz bile enjekte edilmez
aloplop85

6
Bu işe yaramayacak çünkü Job nesneleri oluşturmak quart'lar tarafından yönetiliyor ve bu nedenle alanlar otomatik olarak kablolanmıyor ve sınıf notları ekstra işlem olmadan hiçbir şey yapmıyor.
msangel

1

Yukarıdaki çözüm harika ama benim durumumda enjeksiyon işe yaramadı. Bunun yerine autowireBeanProperties'i kullanmam gerekiyordu, muhtemelen bağlamımın yapılandırılma şekli nedeniyle:

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;
    }
}

1

Bu doğru cevaptır http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030 . ve çoğu insan için çalışacak. Ancak web.xml dosyanız tüm applicationContext.xml dosyalarının farkında değilse, quartz job bu fasulyeleri çağırmayacaktır. Ek applicationContext dosyalarını enjekte etmek için fazladan bir katman yapmak zorunda kaldım

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;
    }
}

Kuvarsınızın bilmesini istediğiniz herhangi bir sayıda bağlam dosyası ekleyebilirsiniz.


0

Emin ol

AutowiringSpringBeanJobFactory extends SpringBeanJobFactory 

bağımlılık çekilir

    "org.springframework:spring-context-support:4..."

ve şuradan DEĞİL

    "org.springframework:spring-support:2..."

Kullanmamı istedi

@Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler)

onun yerine

@Override
protected Object createJobInstance(final TriggerFiredBundle bundle)

bu yüzden iş örneğini otomatik kablolamakta başarısız oluyordu.


0

Projenizde zaten gerçek AspectJ kullandığınızda, iş fasulyesi sınıfına ile not ekleyebilirsiniz @Configurable. O zaman Spring, bu sınıfa, bir yolla inşa edilmiş olsa bile,new


0

Benzer sorunla karşılaştım ve aşağıdaki yaklaşımla ondan çıktım:

<!-- 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>

Yukarıdaki kodda, dao.DAOFramework fasulyesini JobA bean'e enjekte ediyorum ve ExecuteInternal yönteminde, aşağıdaki gibi enjekte edilen fasulyeyi alabilirsiniz:

  daoFramework = (DAOFramework)context.getMergedJobDataMap().get("daoBean");

Umut ediyorum bu yardım eder! Teşekkür ederim.


0

İşlem yöntemlerini çağırmak istediğimde, yukarıdaki tüm bu çözümler Spring 5 ve Hibernate 5 ve Quartz 2.2.3 ile benim için çalışmıyor!

Bu nedenle, zamanlayıcıyı otomatik olarak başlatan ve işleri tetikleyen bu çözümü uyguladım. Bu kodun çoğunu dzone'da buldum . Dinamik olarak tetikleyiciler ve işler oluşturmam gerekmediğinden, statik tetikleyicilerin Yay Yapılandırması aracılığıyla önceden tanımlanmasını ve yalnızca işlerin Yay Bileşenleri olarak sunulmasını istedim.

Temel yapılandırmam şöyle görünüyor

@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;
  }

}

Gördüğünüz gibi, zamanlayıcıya ve bir cron ifadesi aracılığıyla tanımlanan basit bir test tetikleyicisine sahipsiniz. Açıkça istediğiniz zamanlama ifadesini seçebilirsiniz. Daha sonra AutowiringSpringBeanJobFactory'ye ihtiyacınız var.

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);
  }

}

Burada normal uygulama bağlamınızı ve işinizi birbirine bağlarsınız. Bu önemli boşluktur çünkü normalde Quartz, uygulama bağlamınızla bağlantısı olmayan çalışan iş parçacıklarını başlatır. İşlemsel mehtoları yürütememenizin nedeni budur. Eksik olan son şey bir iştir. Öyle görünebilir

@Component
public class CronTest implements Job {

  @Autowired
  private MyService s;

  public CronTest() {
  }

  @Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    s.execute();
  }

}

Mükemmel bir çözüm değil çünkü sadece servis yönteminizi çağırmak için ekstra bir sınıfsınız. Ama yine de işe yarıyor.


0

Jdbc iş mağazası

Jdbc jobstore Quartz kullanıyorsanız farklı bir sınıf yükleyici kullanır. Bu, otomatik kablolama için tüm geçici çözümleri önler, çünkü yaydan gelen nesneler kuvars tarafında uyumlu olmayacaktır, çünkü bunlar farklı bir sınıf yükleyiciden gelmektedir.

Bunu düzeltmek için, varsayılan sınıf yükleyicinin kuvars özellikleri dosyasında şu şekilde ayarlanması gerekir:

org.quartz.scheduler.classLoadHelper.class=org.quartz.simpl.ThreadContextClassLoadHelper

Referans olarak: https://github.com/quartz-scheduler/quartz/issues/221


0

İşinizi basitçe 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());
    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.