ฉีดอ้างอิงถั่วในงานควอตซ์ในฤดูใบไม้ผลิ?


96

ฉันจัดการกำหนดค่าและกำหนดเวลางาน Quartz โดยใช้ร้านค้าถาวร JobStoreTX ใน Spring ฉันไม่ได้ใช้งาน Quartz ของ Spring เพราะฉันต้องการกำหนดเวลาแบบไดนามิกในเวลาทำงานและตัวอย่างทั้งหมดของการรวม Spring กับ Quartz ที่ฉันพบคือการเข้ารหัส shcedules ในไฟล์ config 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;

ไม่ได้ฉีด - การอ้างอิงเป็นโมฆะ ฉันสมมติว่าสิ่งนี้เกิดขึ้นเพราะไม่ใช่ฤดูใบไม้ผลิที่สร้างอินสแตนซ์คลาส EMailJob แต่ Quartz และ Quartz ไม่รู้อะไรเกี่ยวกับการฉีดแบบพึ่งพา ...

ดังนั้นมีวิธีใดบ้างที่จะบังคับให้การฉีดยานี้เกิดขึ้นได้?

ขอบคุณ!

อัปเดต 1: @Aaron: นี่คือส่วนที่เกี่ยวข้องของ 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>

และฉันได้ปรับเปลี่ยนคลาสหลักของฉันเพื่อรับ Scheduler จากโรงงานนี้แทนที่จะเป็น Quartz ':

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

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

แต่เมื่อฉันเรียกใช้แอป - ได้รับข้อผิดพลาดโปรดดูด้านล่าง นี่คือ stacktrace จาก Spring startup ดูเหมือนว่า Scheduler นั้นสร้างขึ้นได้ดี แต่ข้อผิดพลาดเกิดขึ้นเมื่อพยายามสร้างอินสแตนซ์ EMailJob ของฉัน:

2011-08-15 21:49:42,968 [main] INFO  org.springframework.scheduling.quartz.SchedulerFactoryBean - Loading Quartz config from [class path resource [spring/quartz.properties]]
2011-08-15 21:49:43,031 [main] INFO  com.mchange.v2.log.MLog - MLog clients using log4j logging.
2011-08-15 21:49:43,109 [main] INFO  com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
2011-08-15 21:49:43,187 [main] INFO  org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2011-08-15 21:49:43,187 [main] INFO  org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.0.1 created.
2011-08-15 21:49:43,187 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
2011-08-15 21:49:43,187 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
2011-08-15 21:49:43,187 [main] INFO  org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.0.1) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
 Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
 NOT STARTED.
 Currently in standby mode.
 Number of jobs executed: 0
 Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
 Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.

2011-08-15 21:49:43,187 [main] INFO  org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
2011-08-15 21:49:43,187 [main] INFO  org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.0.1
2011-08-15 21:49:43,187 [main] INFO  org.quartz.core.QuartzScheduler - JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@566633
2011-08-15 21:49:43,265 [main] INFO  com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge13f8h1lsg7py1rg0iu0|1956391, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge13f8h1lsg7py1rg0iu0|1956391, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/2010rewrite2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> select 0 from dual, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2011-08-15 21:49:43,343 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
2011-08-15 21:49:43,359 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
2011-08-15 21:49:43,359 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
2011-08-15 21:49:43,359 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
2011-08-15 21:49:43,359 [main] INFO  org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
2011-08-15 21:49:43,359 [main] INFO  org.quartz.core.QuartzScheduler - Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
2011-08-15 21:49:43,562 [schedulerFactoryBean_QuartzSchedulerThread] ERROR org.quartz.core.ErrorLogger - An error occured instantiating job to be executed. job= 'immediateEmailsGroup.DEFAULT.jobFor_1000new1'
org.quartz.SchedulerException: Problem instantiating class  'com.cambridgedata.notifications.EMailJob' -  [See nested exception:  java.lang.AbstractMethodError:  org.springframework.scheduling.quartz.SpringBeanJobFactory.newJob(Lorg/quartz/spi/TriggerFiredBundle;Lorg/quartz/Scheduler;)Lorg/quartz/Job;]
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:141)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:381)
Caused by: java.lang.AbstractMethodError: org.springframework.scheduling.quartz.SpringBeanJobFactory.newJob(Lorg/quartz/spi/TriggerFiredBundle;Lorg/quartz/Scheduler;)Lorg/quartz/Job;
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:134)

ขอบคุณ!

คำตอบ:


131

คุณสามารถใช้สิ่งนี้SpringBeanJobFactoryเพื่อเคลื่อนย้ายวัตถุควอตซ์โดยอัตโนมัติโดยใช้สปริง:

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

ทำงานให้ฉันโดยใช้ spring-3.2.1 และ quartz-2.1.6

ตรวจสอบสรุปสาระสำคัญที่สมบูรณ์แบบที่นี่

ฉันพบวิธีแก้ปัญหาในโพสต์บล็อกนี้


13
คุณควรได้รับรางวัลนี้มันยอดเยี่ยมมาก!
Nathan Feger

2
ทางออกที่ดีจริงๆ! เครดิตทั้งหมดสำหรับผู้เขียนบล็อกโพสต์ :)
jelies

3
ขอบคุณ - สิ่งนี้ช่วยฉันได้หลายวัน! เหตุใด Spring จึงไม่ให้ OOB นี้ นี่เป็นข้อกำหนดพื้นฐานสำหรับการใช้ควอตซ์ในฤดูใบไม้ผลิ
HandyManDan

4
นี่ควรเป็นการใช้งานเริ่มต้น :)
Diego Plentz

2
ทางออกที่ดี แต่ทุกคนมีความคิดว่าเหตุใด AutowireCapableBeanFactory beanFactory จึงถูกทำเครื่องหมายเป็น "ชั่วคราว"? ดูเหมือนว่า SpringBeanJobFactory จะไม่ได้รับการต่อเนื่องดังนั้น beanFactory ก็ไม่จำเป็นต้องทำให้เป็นอนุกรม
Marios

57

ฉันแค่ใส่SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);บรรทัดแรกของJob.execute(JobExecutionContext context)วิธีการของฉัน


7
นี่คือทางออกที่แท้จริง ทดสอบด้วยสปริง 3.2.4 RELEASE และ Quartz 2.2.0 ;)
aloplop85

3
@msangel - ดีทั้งสองอย่างจะใช้ได้ แต่ปัญหาในการใช้ SpringBeanAutowiringSupport ในงาน Quartz ของคุณคือตอนนี้อินสแตนซ์ของงานจำเป็นต้องรู้เกี่ยวกับฤดูใบไม้ผลิซึ่งขัดกับแนวคิดทั้งหมดของ IoC (การฉีด dep) หากตอนนี้คุณจำเป็นต้องใช้ CDI งานควอตซ์ทั้งหมดของคุณจะต้องได้รับการปรับเปลี่ยนแทนที่จะเป็นเพียงโรงงานงานเดียว
demaniak

2
สิ่งนี้ใช้ไม่ได้สำหรับฉันในการทดสอบหน่วยเนื่องจากค้นหาบริบทเว็บแอปพลิเคชัน ฉันต้องใช้คำตอบจาก @jelies
Wim Deblauwe

5
วิธีนี้ใช้ไม่ได้กับสปริง 4.1.4 และควอตซ์ 2.2.1
skywalker

1
ฉันมีปัญหานี้เช่นกันและฉันลองวิธีแก้ปัญหานี้ ใช้งานได้ แต่จะสร้างอินสแตนซ์ใหม่แทนที่จะใช้อินสแตนซ์ที่สร้างไว้แล้ว (ค่าเริ่มต้นซิงเกิลตัน) อย่างไรก็ตามคุณสามารถส่งอะไรก็ได้ไปยังงานของคุณโดยใช้ scheduleler.getContext () ใส่ ("objectName", object);
Krzysztof Cieśliński

13

ปัญหาเดียวกันได้รับการแก้ไขแล้วใน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");

หวังว่าจะช่วยได้ คุณสามารถรับข้อมูลเพิ่มเติมได้จาก บล็อกของ Mark Mclaren


1
ขอบคุณ @Rippon! หลังจากพยายามและล้มเหลวหลายครั้งฉันได้ใช้วิธีการที่คล้ายกันมากที่คุณแนะนำ: ฉันไม่ได้ใช้คุณสมบัติ applicationContextSchedulerContextKey และบริบทของแอปพลิเคชัน แต่ฉันใช้ 'code' <property name = "schedulelerContextAsMap"> <map> <entry key = "mailService" value-ref = "mailService" /> </map> </property>
Marina

8

คุณคิดถูกแล้วเกี่ยวกับ Spring vs. Quartz ที่สร้างอินสแตนซ์ของคลาส อย่างไรก็ตาม Spring มีคลาสบางอย่างที่ให้คุณทำการฉีดพึ่งพาแบบดั้งเดิมใน Quartz ตรวจสอบSchedulerFactoryBean.setJobFactory ()พร้อมกับSpringBeanJobFactory โดยพื้นฐานแล้วโดยการใช้ SpringBeanJobFactory คุณจะเปิดใช้งานการฉีดการพึ่งพาในคุณสมบัติงานทั้งหมด แต่สำหรับค่าที่อยู่ในบริบทตัวกำหนดตารางเวลาควอตซ์หรือแผนที่ข้อมูลงานเท่านั้น ฉันไม่รู้ว่ารูปแบบ DI ทั้งหมดรองรับอะไรบ้าง (ตัวสร้างคำอธิบายประกอบตัวตั้งค่า ... ) แต่ฉันรู้ว่ามันรองรับการฉีดเซ็ตเตอร์


สวัสดีไรอันขอบคุณสำหรับคำแนะนำของคุณ คุณหมายความว่าฉันจะต้องใช้ SpringBeanFactoryJob พร้อมกับทริกเกอร์ที่กำหนดค่าไว้ล่วงหน้าและงานที่ขยาย QuartzJobBean เพื่อเปิดใช้งานการฉีดการพึ่งพาหรือไม่ ปัญหาเกี่ยวกับแนวทางนี้คือทริกเกอร์ถูกกำหนดแบบคงที่ในไฟล์กำหนดค่าของ Spring ซึ่งฉันต้องสามารถกำหนดทริกเกอร์ด้วยกำหนดการแบบไดนามิกในขณะทำงาน ... ดูคำตอบถัดไปของฉันด้านล่างสำหรับรายละเอียดเพิ่มเติม - พื้นที่ไม่เพียงพอใน พื้นที่แสดงความคิดเห็น ...
Marina

@ มาริน่า: ไม่นั่นไม่ใช่วิธีการทำงาน ใช้ SpringBeanJobFactory และทำในแบบที่คุณต้องการ มันก็จะทำงาน นอกจากนี้อย่าโพสต์คำตอบที่เป็นเพียงข้อมูลอัปเดตสำหรับคำถามของคุณ แก้ไขคำถามของคุณแทน
Ryan Stewart

ขอโทษฉันเพิ่งสังเกตเห็นความคิดเห็นของคุณ! ฉันจะลองทำตามที่คุณแนะนำและจะแจ้งให้คุณทราบผล ขอบคุณสำหรับความช่วยเหลือของคุณโอ้ฉันจะพยายามแก้ไขคำถามของฉันแทนที่จะตอบ ...
Marina

7

สำหรับทุกคนที่จะลองสิ่งนี้ในอนาคต

org.springframework.scheduling.quartz.JobDetailBean จัดหาแผนที่ของวัตถุและวัตถุเหล่านั้นอาจเป็นถั่วสปริง

กำหนด smth เช่น

<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"); และคุณพร้อมแล้ว ฉันรู้ว่ามันดูน่าเกลียด แต่วิธีแก้ปัญหาก็ใช้ได้


IMHO ฉันรู้สึกว่าโซลูชันนี้สะอาดและ "เป็นธรรมชาติ" มากกว่าการพยายามเพิ่มความสามารถในการเดินสายอัตโนมัติให้กับงานควอตซ์ดังนั้นฉันจึงไม่คิดว่าจะเป็นวิธีแก้ปัญหา
reallynice

6
ApplicationContext springContext =

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

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

bean.method();

4

ขอบคุณ Rippon! ในที่สุดฉันก็ได้ผลเช่นกันหลังจากการต่อสู้หลายครั้งและวิธีแก้ปัญหาของฉันก็ใกล้เคียงกับที่คุณแนะนำมาก กุญแจสำคัญคือทำให้งานของตัวเองขยาย QuartzJobBean และใช้ตัวกำหนดตารางเวลาบริบทAsMap

ฉันหนีไปโดยไม่ระบุคุณสมบัติ 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" เป็นถั่วบริการของฉันเองซึ่งจัดการโดย 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");
    ....

และการกำหนดค่านี้ยังอนุญาตให้ฉันกำหนดตารางเวลางานแบบไดนามิกโดยใช้โรงงานเพื่อรับ Triggers และ 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");
            ...

ขอบคุณอีกครั้งสำหรับทุกคนที่ช่วยเหลือ

ท่าจอดเรือ


4

วิธีแก้ปัญหาง่ายๆคือการตั้งค่า spring bean ใน Job Data Map จากนั้นดึงข้อมูล 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");

เมื่อพิจารณาว่าข้อมูลงานถูกจัดเก็บเป็นหยด (เมื่อใช้การคงอยู่ของฐานข้อมูล) อาจทำให้เกิดปัญหาด้านประสิทธิภาพของฐานข้อมูล (ลองนึกภาพว่าข้อมูลมีขนาดใหญ่มาก)
Sudip Bhandari

3

นี่คือลักษณะของรหัสเมื่อใช้ @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กำลังเริ่มต้นอยู่หรือไม่? วิธีง่ายๆในการตรวจสอบคือการเพิ่มบรรทัดบันทึกในตัวสร้าง
atrain

@Aaron: ใช่ฉันทำ - แต่เมื่อฉันเพิ่งค้นพบมันกำลังเริ่มต้นสองครั้ง ตามกรอบงาน Spring เอง (และฉันพนันได้เลยว่าอินสแตนซ์นี้มีบริการอีเมลที่ฉีดเข้าไป ... ) และหลังจากนั้นหลังจากที่ Quartz ได้รับการเตรียมใช้งานแล้ว - EMailJob กำลังเริ่มต้นอีกครั้งโดยเฟรมเวิร์ก Quartz - และนั่นคือสิ่งที่ ที่ไม่มีบริการฉีด ... ฉันจะพยายามเพิ่มสแต็กแทร็กของการเริ่มต้นของ Spring โดยแก้ไขคำถามของฉันตามที่ Ryan แนะนำ ...
Marina

2

วิธีแก้ปัญหาจาก Hary https://stackoverflow.com/a/37797575/4252764ทำงานได้ดีมาก ง่ายกว่าไม่ต้องใช้เมล็ดถั่วโรงงานพิเศษมากมายและรองรับทริกเกอร์และงานที่หลากหลาย แค่จะเพิ่มว่างานควอตซ์สามารถทำให้เป็นงานทั่วไปโดยมีงานเฉพาะที่ใช้เป็นถั่วสปริงปกติ

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
  }

}

ขอบคุณ. ผมก็พิจารณาเช่นกัน แต่ในกรณีของฉันฉันกำลังเขียนไลบรารีที่สรุปการใช้ควอตซ์ใด ๆ สิ่งนี้จำเป็นสำหรับการจำชื่อ 'คีย์' เพื่อดึงข้อมูลวัตถุใด ๆ ฉันสามารถทำด้วยวิธีควอตซ์บริสุทธิ์และโพสต์เป็นคำตอบ กรุณาแบ่งปันความคิดของคุณ!
Karthik R

2

นี่เป็นโพสต์เก่าที่ค่อนข้างมีประโยชน์ โซลูชันทั้งหมดที่เสนอทั้งสองข้อนี้มีเงื่อนไขเล็กน้อยที่ไม่เหมาะกับทุกคน:

  • SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); สิ่งนี้ถือว่าหรือกำหนดให้เป็นโครงการที่ใช้สปริงบนเว็บ
  • AutowiringSpringBeanJobFactory วิธีการตามที่กล่าวถึงในคำตอบก่อนหน้านี้มีประโยชน์มาก แต่คำตอบเฉพาะสำหรับผู้ที่ไม่ได้ใช้ api วานิลลาควอตซ์บริสุทธิ์ แต่เป็นกระดาษห่อหุ้มของ Spring เพื่อให้ควอตซ์ทำเช่นเดียวกัน

หากคุณต้องการใช้งานระบบ Quartz อย่างแท้จริงสำหรับการตั้งเวลา (ระบบควอตซ์ที่มีความสามารถในการเดินสายอัตโนมัติด้วยสปริง) ฉันสามารถทำได้ดังนี้:

ฉันต้องการทำแบบควอตซ์ให้มากที่สุดเท่าที่จะเป็นไปได้ดังนั้นการแฮ็กเพียงเล็กน้อยจึงมีประโยชน์

 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ใช้ a โดยปริยายJobFactoryตอนนี้เราจึงเปิดใช้งานโซลูชันแบบต่อสายอัตโนมัติ หวังว่านี่จะช่วยได้!


1

วิธีง่ายๆในการทำก็เพียงแค่ใส่คำอธิบายประกอบในงาน Quartz ด้วย@Componentคำอธิบายประกอบจากนั้น Spring จะทำเวทมนตร์ DI ทั้งหมดให้คุณเนื่องจากตอนนี้ได้รับการยอมรับว่าเป็นถั่วฤดูใบไม้ผลิ ฉันต้องทำบางอย่างที่คล้ายกันสำหรับAspectJแง่มุมหนึ่ง - มันไม่ใช่ Spring bean จนกว่าฉันจะใส่คำอธิบายประกอบด้วย Spring @Componentstereotype


ขอบคุณแอรอนฉันเพิ่งลอง - แต่น่าเสียดายที่ NPE เดียวกันเกิดขึ้น - และบริการอีเมลไม่ได้รับการฉีดเข้าไปในเมล็ดงาน ...
Marina

EmailJobชั้นเรียนของคุณอยู่ในแพ็คเกจที่ Spring จะสแกนเมื่อเริ่มต้นแอปหรือไม่ ความจริงที่ว่าคุณใส่คำอธิบายประกอบ@Componentแต่คลาสที่ฉีดยังคงเป็นโมฆะแสดงว่าไม่มีการสแกนมิฉะนั้น DI เมื่อเริ่มต้นแอปจะทำให้เกิดข้อยกเว้น
atrain

Aaron: ใช่มันควรจะสแกน - ฉันมี <context: component-scan base-package = "com.mybasepackage"> ที่ควรทำ ... ในคำตอบถัดไปของฉันฉันจะให้รหัสหลักทั้งหมดของฉัน ชั้นเรียนพร้อมการกำหนดค่า Spring - ในกรณีที่สามารถมองเห็นสิ่งที่ชัดเจนได้ ...
Marina

ช่องงานที่มีเครื่องหมาย "@Autowired" จะไม่ถูกฉีดเข้าไปแม้ว่าคุณจะทำเครื่องหมายงานด้วย "@Component"
ก็ตาม

6
สิ่งนี้จะใช้ไม่ได้เนื่องจากการสร้างอ็อบเจ็กต์งานได้รับการจัดการโดยควอร์ตดังนั้นฟิลด์จึงไม่ได้ทำงานอัตโนมัติและคำอธิบายประกอบของคลาสจะไม่ทำอะไรเลย
msangel

1

วิธีแก้ปัญหาข้างต้นดีมาก แต่ในกรณีของฉันการฉีดยาไม่ได้ผล ฉันต้องการใช้ 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;
    }
}

1

นี่คือคำตอบที่ถูกhttp://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030 และจะทำงานให้กับคนส่วนใหญ่ แต่ถ้า web.xml ของคุณไม่ทราบไฟล์ applicationContext.xml ทั้งหมดงานควอทซ์จะไม่สามารถเรียกใช้ 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;
    }
}

คุณสามารถเพิ่มไฟล์บริบทจำนวนเท่าใดก็ได้ที่คุณต้องการให้ระบบควอตซ์ของคุณทราบ


0

ตรวจสอบให้แน่ใจว่าไฟล์

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)

ดังนั้นจึงล้มเหลวในการกำหนดอินสแตนซ์งานอัตโนมัติ


0

เมื่อคุณใช้อยู่แล้วจริง AspectJ @Configurableในโครงการของคุณแล้วคุณสามารถใส่คำอธิบายประกอบชั้นถั่วงานที่มี จากนั้นสปริงจะฉีดเข้าไปในคลาสนี้แม้ว่าจะสร้างผ่านก็ตามnew


0

ฉันประสบปัญหาที่คล้ายกันและออกมาจากมันด้วยวิธีการต่อไปนี้:

<!-- 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 bean ลงใน JobA bean และในเมธอด ExecuteInternal คุณจะได้รับการฉีด bean เช่น:

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

ฉันหวังว่ามันจะช่วยได้! ขอขอบคุณ.


0

โซลูชันทั้งหมดข้างต้นใช้ไม่ได้กับฉันกับ Spring 5 และ Hibernate 5 และ Quartz 2.2.3 เมื่อฉันต้องการเรียกใช้วิธีการทำธุรกรรม!

ฉันจึงใช้โซลูชันนี้ซึ่งจะเริ่มตัวกำหนดตารางเวลาและเรียกงานโดยอัตโนมัติ ผมพบว่าจำนวนมากของรหัสที่ที่DZone เนื่องจากฉันไม่จำเป็นต้องสร้างทริกเกอร์และงานแบบไดนามิกฉันต้องการให้ทริกเกอร์แบบคงที่กำหนดไว้ล่วงหน้าผ่าน Spring Configuration และเฉพาะงานที่จะเปิดเผยเป็น Spring Components

การกำหนดค่าพื้นฐานของฉันมีลักษณะเช่นนี้

@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 จะเริ่มต้นเป็นเธรดของผู้ปฏิบัติงานซึ่งไม่มีการเชื่อมต่อกับบริบทแอปพลิเคชันของคุณ นั่นคือเหตุผลว่าทำไมคุณไม่สามารถดำเนินการ Mehtods ธุรกรรมได้ สิ่งสุดท้ายที่ขาดหายไปคืองาน มันสามารถมีลักษณะเช่นนั้น

@Component
public class CronTest implements Job {

  @Autowired
  private MyService s;

  public CronTest() {
  }

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

}

ไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์แบบเพราะคุณเป็นชั้นเรียนพิเศษสำหรับการเรียกใช้วิธีการบริการของคุณเท่านั้น แต่อย่างไรก็ตามมันได้ผล


0

Jdbc jobstore

หากคุณใช้ jdbc jobstore Quartz ให้ใช้ classloader อื่น ซึ่งจะป้องกันวิธีแก้ปัญหาทั้งหมดสำหรับการเดินสายอัตโนมัติเนื่องจากวัตถุจากสปริงจะไม่สามารถทำงานร่วมกันได้ที่ด้านควอตซ์เนื่องจากมีต้นกำเนิดจากตัวโหลดคลาสอื่น

ในการแก้ไขนั้นต้องตั้งค่า classloader เริ่มต้นในไฟล์คุณสมบัติควอตซ์ดังนี้:

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

อ้างอิง: https://github.com/quartz-scheduler/quartz/issues/221


0

เพียงแค่ขยายงานของคุณจาก 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());
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.