tiêm tham chiếu đậu vào một công việc Thạch anh vào mùa xuân?


92

Tôi đã quản lý để định cấu hình và lên lịch một công việc Quartz bằng cách sử dụng cửa hàng liên tục JobStoreTX trong Spring. Tôi không sử dụng các công việc Quartz của Spring, vì tôi cần lên lịch cho chúng động, vào thời gian chạy và tất cả các ví dụ về tích hợp Spring với Quartz mà tôi tìm thấy đều là mã hóa cứng các shcedules trong các tệp cấu hình Spring ... Dù sao, đây là cách Tôi lên lịch công việc:

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 là một công việc đơn giản là gửi e-mail bằng cách sử dụng lớp JavaMailSenderImpl của 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");

}

Vấn đề là tôi cần tham chiếu đến một phiên bản của lớp này (JavaMailSenderImpl) trong lớp EMailJob của tôi. Khi tôi cố gắng tiêm nó như thế này:

@Autowired
private JavaMailSenderImpl mailSenderImpl;

nó không được đưa vào - tham chiếu là NULL. Tôi cho rằng điều này đang xảy ra bởi vì không phải Spring là người khởi tạo lớp EMailJob, mà là Quartz và Quartz không biết bất cứ điều gì về tiêm phụ thuộc ...

Vì vậy, có một số cách để buộc điều này xảy ra?

cảm ơn!

Cập nhật 1: @Aaron: đây là một phần có liên quan của stacktrace từ khi khởi động, cho thấy EMailJob đã được khởi tạo hai lần:

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

cảm ơn!

Cập nhật # 2: @Ryan:

Tôi đã cố gắng sử dụng SpringBeanJobFactory như sau:

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

Và tôi đã sửa đổi lớp chính của mình để lấy Bộ lập lịch từ nhà máy này, thay vì Thạch anh ':

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

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

Nhưng khi tôi chạy ứng dụng - gặp lỗi, hãy xem bên dưới. Đây là stacktrace từ khởi động Spring. Có vẻ như bản thân Trình lập lịch được tạo tốt, nhưng lỗi xảy ra khi nó đang cố gắng khởi tạo EMailJob của tôi:

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)

cảm ơn!

Câu trả lời:


129

Bạn có thể sử dụng điều này SpringBeanJobFactoryđể tự động truyền động các đối tượng thạch anh bằng cách sử dụng lò xo:

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

Sau đó, đính kèm nó vào của bạn SchedulerBean(trong trường hợp này là với Java-config):

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

    ...

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

    ...

    return quartzScheduler;
}

Làm việc cho tôi, sử dụng spring-3.2.1 và quartz-2.1.6.

Kiểm tra ý chính đầy đủ ở đây .

Tôi đã tìm thấy giải pháp trong bài đăng trên blog này


13
Bạn sẽ giành được một giải thưởng cho điều này, nó là tuyệt vời!
Nathan Feger

2
Giải pháp nó thực sự tuyệt vời! Tất cả các khoản tín dụng cho tác giả của bài đăng trên blog :)
jelies Ngày

3
Cảm ơn - điều này đã cứu tôi nhiều ngày! Tại sao Spring không cung cấp OOB này. Đây là yêu cầu rất cơ bản để sử dụng Thạch anh mùa xuân.
HandyManDan

4
điều này cần được thực hiện mặc định :)
Diego Plentz

2
giải pháp tuyệt vời, nhưng bất cứ ai có bất kỳ ý tưởng tại sao AutowireCapableBeanFactory beanFactory được đánh dấu là "tạm thời"? AutowiringSpringBeanJobFactory dường như không được tuần tự anyway, do đó không phải sẽ BeanFactory bao giờ cần phải được tuần tự
Marios

57

Tôi chỉ đặt SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);dòng đầu tiên của Job.execute(JobExecutionContext context)phương pháp của tôi .


7
Đây là giải pháp thực sự. Kiểm tra với Spring 3.2.4.RELEASE và Quartz 2.2.0. ;)
aloplop85 09/09

3
@msangel - tốt, cả hai đều SẼ hoạt động, nhưng vấn đề với việc sử dụng SpringBeanAutowiringSupport trong công việc Quartz của bạn, là trường hợp công việc bây giờ cần biết về Spring, điều này đi ngược lại toàn bộ ý tưởng của IoC (dep injection). Ví dụ: nếu bây giờ bạn cần sử dụng CDI, tất cả các công việc thạch anh của bạn sẽ cần được điều chỉnh, thay vì chỉ một nhà máy công việc.
demaniak

2
Điều này không hiệu quả đối với tôi trong một bài kiểm tra đơn vị vì nó tìm kiếm ngữ cảnh ứng dụng web. Tôi đã phải sử dụng câu trả lời từ @jelies
Wim Deblauwe

5
Giải pháp này không làm việc với mùa xuân 4.1.4 và thạch anh 2.2.1
Skywalker

1
Tôi cũng gặp sự cố này và tôi đã thử giải pháp này. Nó hoạt động NHƯNG nó tạo ra một thể hiện mới thay vì sử dụng một phiên bản đã được tạo (singleton mặc định). Dù sao đi nữa, bạn có thể chuyển bất cứ thứ gì cho công việc của mình bằng cách sử dụng Scheduler.getContext (). Put ("objectName", object);
Krzysztof Cieśliński

13

Vấn đề tương tự đã được giải quyết trong LINK :

Tôi có thể tìm thấy tùy chọn khác từ bài đăng trên diễn đàn Spring mà bạn có thể chuyển tham chiếu đến ngữ cảnh ứng dụng Spring thông qua SchedulerFactoryBean. Giống như ví dụ được hiển thị bên dưới:

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

Sau đó, sử dụng mã bên dưới trong lớp công việc của bạn, bạn có thể tải ứng dụngContext và nhận bất kỳ hạt đậu nào bạn muốn.

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

Hy vọng nó giúp. Bạn có thể lấy thêm thông tin từ Mark Mclaren'sBlog


1
Cảm ơn, @Rippon! Sau nhiều lần thử và không thành công, tôi đã sử dụng một cách tiếp cận tương tự mà bạn đã đề xuất: Tôi không sử dụng thuộc tính applicationContextSchedulerContextKey và ngữ cảnh ứng dụng, nhưng tôi đã sử dụng 'mã' <property name = "SchedulerContextAsMap"> <map> <entry key = "mailService" value-ref = "mailService" /> </map> </property>
Marina

8

Bạn đã đúng trong giả định của mình về Spring so với Quartz khởi tạo lớp. Tuy nhiên, Spring cung cấp một số lớp cho phép bạn thực hiện một số thao tác tiêm phụ thuộc nguyên thủy trong Quartz. Kiểm tra SchedulerFactoryBean.setJobFactory () cùng với SpringBeanJobFactory . Về cơ bản, bằng cách sử dụng SpringBeanJobFactory, bạn bật tính năng chèn phụ thuộc vào tất cả các thuộc tính Công việc, nhưng chỉ cho các giá trị nằm trong ngữ cảnh của bộ lập lịch Quartz hoặc bản đồ dữ liệu công việc . Tôi không biết tất cả các kiểu DI mà nó hỗ trợ (hàm tạo, chú thích, setter ...) nhưng tôi biết nó hỗ trợ chèn setter.


Xin chào, Ryan, cảm ơn vì những đề xuất của bạn. Ý bạn là tôi sẽ phải sử dụng SpringBeanFactoryJob, cùng với các Trình kích hoạt được cấu hình sẵn và các công việc mở rộng QuartzJobBean để kích hoạt tính năng tiêm phụ thuộc? Vấn đề với cách tiếp cận này là các trình kích hoạt được xác định tĩnh trong tệp cấu hình của Spring, nơi tôi cần có thể xác định trình kích hoạt với lịch động tại thời gian chạy ... Xem câu trả lời tiếp theo của tôi bên dưới để biết thêm chi tiết - không đủ dung lượng trong khu vực bình luận ...
Marina

@Marina: Không, đó không phải là cách nó hoạt động. Sử dụng SpringBeanJobFactory và thực hiện theo cách bạn muốn. Nó sẽ chỉ hoạt động. Ngoài ra, không đăng câu trả lời chỉ là cập nhật cho câu hỏi của bạn. Thay vào đó, hãy chỉnh sửa câu hỏi của bạn.
Ryan Stewart

xin lỗi, tôi chỉ nhận thấy bình luận của bạn! Tôi sẽ dùng thử theo gợi ý của bạn và sẽ cho bạn biết kết quả. Cảm ơn sự giúp đỡ của bạn! Ồ và tôi sẽ cố gắng chỉnh sửa câu hỏi của mình thay vì trả lời ...
Marina

7

cho tất cả những ai sẽ thử điều này trong tương lai.

org.springframework.scheduling.quartz.JobDetailBean cung cấp bản đồ của các đối tượng và những đối tượng đó có thể là hạt đậu mùa xuân.

xác định smth như thế nào

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

và sau đó, bên trong

public void executeInternal(JobExecutionContext context)

gọi myBean = (myBean) context.getMergedJobDataMap().get("myBean"); và tất cả các bạn đã thiết lập. Tôi biết, nó trông xấu xí, nhưng như một cách giải quyết, nó hoạt động


IMHO Tôi cảm thấy giải pháp này sạch sẽ và "tự nhiên" hơn là cố gắng thêm khả năng tự động nạp vào các công việc thạch anh, vì vậy tôi không nghĩ đó là một công việc xung quanh.
reallynice

6
ApplicationContext springContext =

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

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

bean.method();

4

Cảm ơn, Rippon! Cuối cùng tôi cũng đã làm được điều này, sau nhiều lần đấu tranh và giải pháp của tôi rất gần với những gì bạn đề xuất! Chìa khóa là thực hiện Công việc của riêng tôi để mở rộng QuartzJobBean và sử dụng lập lịchContextAsMap.

Tôi đã thoát ra mà không chỉ định thuộc tính applicationContextSchedulerContextKey - nó hoạt động mà không cần đến tôi.

Vì lợi ích của những người khác, đây là cấu hình cuối cùng phù hợp với tôi:

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

Lưu ý rằng bean 'mailService' là bean dịch vụ của riêng tôi, do Spring quản lý. Tôi có thể truy cập nó trong Công việc của mình như sau:

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

Và cấu hình này cũng cho phép tôi lên lịch động cho các công việc, bằng cách sử dụng các nhà máy để lấy các Trigger và JobDetails và thiết lập các thông số cần thiết trên chúng theo lập trình:

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

Một lần nữa cảm ơn rất nhiều đến tất cả những người đã giúp đỡ,

Bến du thuyền


4

Một giải pháp đơn giản là đặt spring bean trong Bản đồ dữ liệu công việc và sau đó truy xuất bean trong lớp công việc, chẳng hạn

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

xem xét rằng dữ liệu công việc được lưu giữ như blob (khi sử dụng db kiên trì) này có thể dẫn đến vấn đề hiệu suất db (Hãy tưởng tượng dữ liệu thực sự khổng lồ)
Sudip Bhandari

3

Đây là mã trông như thế nào với @Component:

Lớp chính lên lịch công việc:

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 giống như trong bài đăng đầu tiên của tôi ngoại trừ chú thích @Component:

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

Và tệp cấu hình của Spring có:

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

Cảm ơn vì sự giúp đỡ!

Bến du thuyền


Khi ứng dụng của bạn khởi động, bạn có thấy EmailJobđang được khởi chạy không? Một cách dễ dàng để kiểm tra là thêm một dòng nhật ký trong hàm tạo.
atrain

@Aaron: vâng, tôi có - nhưng như tôi vừa phát hiện, nó đang được khởi tạo hai lần! Một lần bởi chính khuôn khổ Spring (và tôi cá rằng trường hợp này có dịch vụ thư được đưa vào nó ...) và sau đó, sau khi bản thân Quartz được khởi tạo - EMailJob đang được khởi tạo lại bởi khuôn khổ Quartz - và đó là điều đó không có dịch vụ được đưa vào ... Tôi sẽ cố gắng thêm dấu vết ngăn xếp về khởi động của Spring bằng cách chỉnh sửa câu hỏi của tôi, như Ryan đã đề xuất ...
Marina

2

Giải pháp từ Hary https://stackoverflow.com/a/37797575/4252764 hoạt động rất tốt. Nó đơn giản hơn, không cần quá nhiều factory bean đặc biệt và hỗ trợ nhiều trình kích hoạt và công việc. Chỉ xin nói thêm rằng công việc Quartz có thể được thực hiện chung chung, với các công việc cụ thể được thực hiện như các loại đậu Spring thông thường.

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
  }

}

Cảm ơn. Tôi cũng đã cân nhắc điều đó. Nhưng trong trường hợp của tôi, tôi đang viết một thư viện tóm tắt bất kỳ triển khai thạch anh nào. Điều này cần thiết để ghi nhớ tên 'khóa' để truy xuất bất kỳ đối tượng nào. Tôi đã có thể làm điều đó theo cách thạch anh tinh khiết và chỉ đăng nó như một câu trả lời. Xin chia sẻ suy nghĩ của bạn!
Karthik R

1

Một cách đơn giản để làm điều đó là chỉ cần chú thích các Công việc thạch anh bằng @Componentchú thích, và sau đó Spring sẽ thực hiện tất cả phép thuật DI cho bạn, vì nó hiện được công nhận là một hạt đậu Mùa xuân. Tôi phải làm điều gì đó tương tự cho một AspectJkhía cạnh - nó không phải là một hạt đậu Mùa xuân cho đến khi tôi chú thích nó bằng @Componentkhuôn mẫu Mùa xuân .


Cảm ơn, Aaron, tôi vừa thử - nhưng thật không may, NPE tương tự cũng xảy ra - và dịch vụ thư không được đưa vào công việc ...
Marina

EmailJobLớp của bạn có nằm trong một gói sẽ được Spring quét khi khởi động ứng dụng không? Thực tế là bạn đã chú thích @Componentnhưng lớp được đưa vào vẫn là null cho thấy rằng nó không được quét - nếu không, DI khi khởi động ứng dụng sẽ ném ra một ngoại lệ.
atrain

Aaron: vâng, nó phải được quét - Tôi có <context: component-scan base-package = "com.mybasepackage"> nên làm điều đó ... Trong câu trả lời tiếp theo của tôi, tôi sẽ cung cấp mã đầy đủ của chính lớp, với cấu hình Spring - đề phòng trường hợp có thể phát hiện ra điều gì đó rõ ràng ...
Marina

Các lĩnh vực công việc được đánh dấu bằng "@Autowired" không được tiêm ngay cả khi bạn đánh dấu các công việc với "@Component"
aloplop85

6
Điều này sẽ không hoạt động vì việc tạo các đối tượng Công việc được quản lý bằng quart và do đó các trường không được tự động tải và chú thích lớp không làm gì nếu không có thêm xử lý.
msangel

1

Đây là câu trả lời đúng http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030 . và sẽ làm việc cho hầu hết mọi người. Nhưng nếu web.xml của bạn không nhận biết được tất cả các tệp applicationContext.xml, thì công việc thạch anh sẽ không thể gọi các bean đó. Tôi đã phải thực hiện thêm một lớp để đưa vào các tệp ứng dụng bổ sung

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

Bạn có thể thêm bất kỳ số lượng tệp ngữ cảnh nào mà bạn muốn thạch anh của mình biết.


1

Đây là một bài viết khá cũ vẫn còn hữu ích. Tất cả các giải pháp đề xuất hai điều này đều có ít điều kiện không phù hợp với tất cả:

  • SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); Điều này giả định hoặc yêu cầu nó phải là một dự án dựa trên web mùa xuân
  • AutowiringSpringBeanJobFactory cách tiếp cận dựa trên được đề cập trong câu trả lời trước là rất hữu ích, nhưng câu trả lời dành riêng cho những người không sử dụng api thạch anh vani nguyên chất mà thay vào đó là vỏ bọc của Spring cho thạch anh để làm điều tương tự.

Nếu bạn muốn duy trì triển khai Quartz thuần túy để lập lịch (Quartz với khả năng Autowiring với Spring), tôi có thể thực hiện như sau:

Tôi đã tìm cách làm điều đó theo cách thạch anh càng nhiều càng tốt và do đó, ít vụ hack chứng tỏ hữu ích.

 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);cung cấp cho chúng tôi một ví dụ công việc tự động mong muốn. Kể từ khi AutowiringSpringBeanJobFactorytriển khai ngầm định a JobFactory, chúng tôi hiện đã kích hoạt giải pháp có thể nối dây tự động. Hi vọng điêu nay co ich!


0

Đảm bảo

AutowiringSpringBeanJobFactory extends SpringBeanJobFactory 

sự phụ thuộc được kéo từ

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

và KHÔNG từ

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

Nó muốn tôi sử dụng

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

thay vì

@Override
protected Object createJobInstance(final TriggerFiredBundle bundle)

vì vậy đã không thể tự động truyền tải phiên bản công việc.


0

Khi bạn đã sử dụng AspectJ thực trong dự án của mình, thì bạn có thể chú thích lớp job bean với @Configurable. Sau đó, Spring sẽ đưa vào lớp này, ngay cả khi nó được xây dựng thông quanew


0

Tôi đã đối mặt với vấn đề tương tự và thoát ra khỏi nó với cách tiếp cận sau:

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

Trong đoạn mã trên, tôi đưa dao.DAOFramework bean vào JobA bean và trong phương thức ExecuteInternal bên trong, bạn có thể tiêm bean như:

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

Tôi hy vọng nó sẽ giúp! Cảm ơn bạn.


0

Giải pháp trên là tuyệt vời nhưng trong trường hợp của tôi, thuốc tiêm không hoạt động. Thay vào đó, tôi cần sử dụng autowireBeanProperties, có thể do cách cấu hình ngữ cảnh của tôi:

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

0

Tất cả các giải pháp trên không hoạt động với tôi với Spring 5 và Hibernate 5 và Quartz 2.2.3 khi tôi muốn gọi các phương thức giao dịch!

Do đó, tôi đã triển khai giải pháp này để tự động khởi động bộ lập lịch và kích hoạt công việc. Tôi đã tìm thấy rất nhiều mã đó tại dzone . Bởi vì tôi không cần tạo các trình kích hoạt và công việc động, tôi muốn các trình kích hoạt tĩnh được xác định trước thông qua Cấu hình mùa xuân và chỉ các công việc được hiển thị dưới dạng Thành phần mùa xuân.

Cấu hình cơ bản của tôi trông như thế này

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

}

Như bạn có thể thấy, bạn có bộ lập lịch và một trình kích hoạt kiểm tra đơn giản được xác định thông qua biểu thức cron. Rõ ràng là bạn có thể chọn bất kỳ biểu thức lập lịch nào bạn thích. Sau đó, bạn cần AutowiringSpringBeanJobFactory giống như thế này

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

}

Ở đây, bạn kết nối bối cảnh ứng dụng bình thường và công việc của bạn với nhau. Đây là khoảng trống quan trọng vì thông thường Quartz bắt đầu đó là các luồng công nhân không có kết nối với ngữ cảnh ứng dụng của bạn. Đó là lý do tại sao bạn không thể thực hiện mehtod Giao dịch. Điều cuối cùng còn thiếu là một công việc. Nó có thể trông như thế

@Component
public class CronTest implements Job {

  @Autowired
  private MyService s;

  public CronTest() {
  }

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

}

Nó không phải là một giải pháp hoàn hảo bởi vì bạn là một lớp bổ sung chỉ để gọi phương thức dịch vụ của bạn. Nhưng tuy nhiên nó vẫn hoạt động.


0

Kho việc làm jdbc

Nếu bạn đang sử dụng kho công việc jdbc, Quartz sử dụng một trình nạp lớp khác. Điều đó ngăn chặn tất cả các giải pháp thay thế cho tự động nạp, vì các đối tượng từ mùa xuân sẽ không tương thích ở phía thạch anh, bởi vì chúng bắt nguồn từ một trình nạp lớp khác.

Để khắc phục điều đó, bộ nạp lớp mặc định phải được đặt trong tệp thuộc tính thạch anh như sau:

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

Tham khảo: https://github.com/quartz-scheduler/quartz/issues/221


0

Đơn giản chỉ cần mở rộng công việc của bạn từ 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());
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.