J'ai réussi à configurer et à planifier un travail Quartz à l'aide du magasin persistant JobStoreTX au printemps. Je n'utilise pas les travaux Quartz de Spring, car je dois les planifier dynamiquement, au moment de l'exécution, et tous les exemples d'intégration de Spring avec Quartz que j'ai trouvés codaient en dur les shcedules dans les fichiers de configuration Spring ... Quoi qu'il en soit, voici comment Je planifie le travail:
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 est un travail simple qui envoie des e-mails à l'aide de la classe JavaMailSenderImpl de 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");
}
Le problème est que j'ai besoin d'obtenir une référence à une instance de cette classe (JavaMailSenderImpl) dans ma classe EMailJob. Quand j'essaye de l'injecter comme ceci:
@Autowired
private JavaMailSenderImpl mailSenderImpl;
il n'est pas injecté - la référence est NULL. Je suppose que cela se produit car ce n'est pas Spring qui instancie la classe EMailJob, mais Quartz et Quartz ne savent rien de l'injection de dépendances ...
Alors, y a-t-il un moyen de forcer cette injection à se produire?
Merci!
Mise à jour 1: @Aaron: voici une partie pertinente de la trace de pile depuis le démarrage, qui montre que EMailJob a été instancié deux fois:
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 ...
Merci!
Mise à jour n ° 2: @Ryan:
J'ai essayé d'utiliser SpringBeanJobFactory comme suit:
<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>
Et j'ai modifié ma classe principale pour obtenir Scheduler de cette usine, au lieu de Quartz ':
@PostConstruct
public void initNotificationScheduler() {
try {
//sf = new StdSchedulerFactory("spring/quartz.properties");
//scheduler = sf.getScheduler();
scheduler = schedulerFactoryBean.getScheduler();
scheduler.start();
....
Mais lorsque j'exécute l'application - obtenir des erreurs, voir ci-dessous. Voici le stacktrace du démarrage de Spring. On dirait que le planificateur lui-même est bien créé, mais l'erreur survient lorsqu'il essaie d'instancier mon 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)
Merci!
la source
Je viens de mettre
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
en première ligne de maJob.execute(JobExecutionContext context)
méthode.la source
Le même problème a été résolu dans LINK :
J'ai pu trouver une autre option à partir de la publication sur le forum Spring selon laquelle vous pouvez transmettre une référence au contexte de l'application Spring via SchedulerFactoryBean. Comme l'exemple ci-dessous:
Ensuite, en utilisant le code ci-dessous dans votre classe de travail, vous pouvez obtenir l'applicationContext et obtenir le bean de votre choix.
J'espère que ça aide. Vous pouvez obtenir plus d'informations sur le blog de Mark Mclaren
la source
Vous avez raison dans votre hypothèse sur l'instanciation de la classe par Spring vs Quartz. Cependant, Spring fournit des classes qui vous permettent d'effectuer des injections de dépendances primitives dans Quartz. Découvrez SchedulerFactoryBean.setJobFactory () avec SpringBeanJobFactory . Essentiellement, en utilisant SpringBeanJobFactory, vous activez l'injection de dépendances sur toutes les propriétés du Job, mais uniquement pour les valeurs qui se trouvent dans le contexte du planificateur Quartz ou dans la mappe de données du job . Je ne sais pas ce que tous les styles DI qu'il prend en charge (constructeur, annotation, setter ...) mais je sais qu'il prend en charge l'injection de setter.
la source
pour tous ceux qui essaieront cela à l'avenir.
org.springframework.scheduling.quartz.JobDetailBean fournit une carte des objets et ces objets peuvent être des haricots printaniers.
définir smth comme
et puis, à l'intérieur
appelez
myBean = (myBean) context.getMergedJobDataMap().get("myBean");
et vous êtes tous ensemble. Je sais, ça a l'air moche, mais comme solution de contournement ça marchela source
la source
Merci, Rippon! J'ai enfin réussi à faire fonctionner cela aussi, après de nombreuses luttes, et ma solution est très proche de ce que vous avez suggéré! La clé était de créer mon propre Job pour étendre QuartzJobBean et d'utiliser le schedulerContextAsMap.
Je me suis échappé sans spécifier la propriété applicationContextSchedulerContextKey - cela a fonctionné sans cela pour moi.
Pour le bénéfice des autres, voici la configuration finale qui a fonctionné pour moi:
Notez que le bean 'mailService "est mon propre bean de service, géré par Spring. J'ai pu y accéder dans mon Job comme suit:
Et cette configuration m'a également permis de planifier dynamiquement les travaux, en utilisant des usines pour obtenir les déclencheurs et les détails des tâches et en définissant les paramètres requis sur eux par programme:
Merci encore à tous ceux qui ont aidé,
Marina
la source
Une solution simple consiste à définir le bean spring dans la mappe de données de travail, puis à récupérer le bean dans la classe de travail, par exemple
»
la source
Voici à quoi ressemble le code avec @Component:
Classe principale qui planifie le travail:
L'EmailJob est le même que dans ma première publication à l'exception de l'annotation @Component:
Et le fichier de configuration du Spring contient:
Merci pour votre aide!
Marina
la source
EmailJob
être initialisé? Un moyen simple de vérifier est d'ajouter une ligne de journal dans le constructeur.Une solution de Hary https://stackoverflow.com/a/37797575/4252764 fonctionne très bien. C'est plus simple, ne nécessite pas autant de beans usine spéciaux et prend en charge plusieurs déclencheurs et tâches. J'ajouterais simplement que le travail Quartz peut être rendu générique, avec des travaux spécifiques implémentés en tant que beans Spring ordinaires.
la source
C'est un article assez ancien qui est toujours utile. Toutes les solutions que propose ces deux ont peu de condition qui ne conviennent pas à toutes:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
Cela suppose ou nécessite que ce soit un projet basé sur le printempsAutowiringSpringBeanJobFactory
L'approche basée sur l'approche mentionnée dans la réponse précédente est très utile, mais la réponse est spécifique à ceux qui n'utilisent pas l'api de quartz vanille pur mais plutôt le wrapper de Spring pour que le quartz fasse de même.Si vous souhaitez rester avec une implémentation pure de Quartz pour la planification (Quartz avec capacités de câblage automatique avec Spring), j'ai pu le faire comme suit:
Je cherchais à le faire autant que possible de manière quartz et donc peu de piratage s'avère utile.
quartzScheduler.setJobFactory(autowiringSpringBeanJobFactory);
nous donne une instance de travail auto-câblée. PuisqueAutowiringSpringBeanJobFactory
implémente implicitement aJobFactory
, nous avons maintenant activé une solution auto-câblable. J'espère que cela t'aides!la source
Un moyen simple de le faire serait d'annoter simplement les Jobs Quartz avec une
@Component
annotation, puis Spring fera toute la magie DI pour vous, car il est maintenant reconnu comme un haricot de printemps. J'ai dû faire quelque chose de similaire pour unAspectJ
aspect - ce n'était pas un haricot de printemps jusqu'à ce que je l'ai annoté avec le@Component
stéréotype de printemps .la source
EmailJob
classe est-elle dans un package qui serait analysé par Spring au démarrage de l'application? Le fait que vous ayez annoté avec@Component
mais que la classe injectée soit toujours nulle indique qu'elle n'est pas analysée - sinon la DI au démarrage de l'application lèverait une exception.C'est la bonne réponse http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030 . et fonctionnera pour la plupart des gens. Mais si votre web.xml ne connaît pas tous les fichiers applicationContext.xml, le travail quartz ne pourra pas appeler ces beans. J'ai dû faire une couche supplémentaire pour injecter des fichiers d'application supplémentaires
Vous pouvez ajouter autant de fichiers contextuels que vous voulez que votre quartz prenne connaissance.
la source
Assurez-vous que votre
la dépendance est tirée de
et PAS de
Il voulait que j'utilise
au lieu de
ainsi échouait à l'instance de travail d'autowire.
la source
Lorsque vous utilisez déjà le vrai AspectJ dans votre projet, vous pouvez annoter la classe de bean de travail avec
@Configurable
. Ensuite, Spring injectera dans cette classe, même si elle est construite vianew
la source
J'ai fait face au problème similaire et en suis sorti avec l'approche suivante:
Dans le code ci-dessus, j'injecte le bean dao.DAOFramework dans le bean JobA et dans la méthode ExecuteInternal, vous pouvez obtenir un bean injecté comme:
J'espère que ça aide! Je vous remercie.
la source
La solution ci-dessus est excellente mais dans mon cas, l'injection ne fonctionnait pas. J'avais besoin d'utiliser autowireBeanProperties à la place, probablement en raison de la façon dont mon contexte est configuré:
la source
Toutes ces solutions ci-dessus ne fonctionnent pas pour moi avec Spring 5 et Hibernate 5 et Quartz 2.2.3 lorsque je veux appeler des méthodes transactionnelles!
J'ai donc implémenté cette solution qui démarre automatiquement l'ordonnanceur et déclenche les jobs. J'ai trouvé beaucoup de ce code sur dzone . Comme je n'ai pas besoin de créer des déclencheurs et des travaux de manière dynamique, je voulais que les déclencheurs statiques soient prédéfinis via Spring Configuration et que seuls les travaux soient exposés en tant que composants Spring.
Ma configuration de base ressemble à ceci
Comme vous pouvez le voir, vous avez le planificateur et un simple déclencheur de test qui est défini via une expression cron. Vous pouvez évidemment choisir l'expression de planification que vous souhaitez. Vous avez alors besoin de l'AutowiringSpringBeanJobFactory qui va comme ceci
Ici, vous connectez votre contexte d'application normal et votre travail. C'est l'écart important car, normalement, Quartz démarre ses threads de travail qui n'ont aucune connexion avec votre contexte d'application. C'est la raison pour laquelle vous ne pouvez pas exécuter de mehtods transactionnels. La dernière chose qui manque est un travail. Ça peut ressembler à ça
Ce n'est pas une solution parfaite car vous êtes une classe supplémentaire uniquement pour appeler votre méthode de service. Mais néanmoins cela fonctionne.
la source
Jobstore Jdbc
Si vous utilisez le jobstore jdbc, Quartz utilise un chargeur de classe différent. Cela empêche toutes les solutions de contournement pour le câblage automatique, car les objets du ressort ne seront pas compatibles côté quartz, car ils proviennent d'un chargeur de classe différent.
Pour résoudre ce problème, le chargeur de classe par défaut doit être défini dans le fichier de propriétés quartz comme ceci:
Comme référence: https://github.com/quartz-scheduler/quartz/issues/221
la source
Prolongez simplement votre travail de
QuartzJobBean
la source