2015-01-25 14 views
8

In un'applicazione, dal momento che l'ho convertita da una classica webapp di Spring (distribuita in un sistema Tomcat) a un'applicazione Spring Boot (V1.2.1), devo affrontare il problema I lavori programmati al quarzo non funzionano più.Spring Boot: utilizzo di un servizio @Service in Quartz

ho pianificare questi lavori al quarzo in questo modo:

// My own Schedule object which holds data about what to schedule when 
Schedule schedule = scheduleService.get(id of the schedule); 

String scheduleId = schedule.getId(); 

JobKey jobKey = new JobKey(scheduleId); 
TriggerKey triggerKey = new TriggerKey(scheduleId); 

JobDataMap jobData = new JobDataMap(); 
jobData.put("scheduleId", scheduleId); 

JobBuilder jobBuilder = JobBuilder.newJob(ScheduledActionRunner.class) 
    .withIdentity(jobKey) 
    .withDescription(schedule.getName()) 
    .usingJobData(jobData); 

JobDetail job = jobBuilder.build(); 

TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger() 
    .forJob(jobKey) 
    .withIdentity(triggerKey) 
    .withDescription(schedule.getName()); 

triggerBuilder = triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(schedule.toCronExpression())); 

Trigger trigger = triggerBuilder.build(); 

org.quartz.Scheduler scheduler = schedulerFactoryBean.getScheduler(); 

scheduler.scheduleJob(job, trigger); 

ScheduledActionRunner:

@Component 
public class ScheduledActionRunner extends QuartzJobBean { 

    @Autowired 
    private ScheduleService scheduleService; 

    public ScheduledActionRunner() { 
    } 

    @Override 
    public void executeInternal(final JobExecutionContext context) throws JobExecutionException { 
     SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); 
     final JobDataMap jobDataMap = context.getMergedJobDataMap(); 
     final String scheduleId = jobDataMap.getString("scheduleId"); 
     final Schedule schedule = scheduleService.get(scheduleId); 
     // here it goes BANG since scheduleService is null 
    } 
} 

ScheduleService è un servizio Primavera classico, che recupera i dati da Hibernate. Come ho detto sopra, questo ha funzionato bene fino a quando non mi sono trasferito a Spring Boot.

Quando ho implementato questo codice con la classica applicazione Spring, SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); ha fatto il trucco per occuparsi dell'autowiring del servizio.

Cosa è necessario per eseguire nuovamente questo lavoro nell'ambiente Spring Boot?

Edit:

Alla fine ho scelto di allontanarsi da utilizzando Quarzo a favore del codice ThreadPoolTaskScheduler.The di primavera era molto semplificata e funziona come previsto.

+0

Commento di rispondere da Dewfy (dal momento che non ho abbastanza rep per commentare): Ho anche dovuto contrassegnare il metodo con l'annotazione @Transactional, poiché ho ricevuto l'errore che la sessione di sospensione non era collegata. – gooboo

risposta

11

SpringBeanAutowiringSupport utilizza il contesto dell'applicazione Web, che non è disponibile nel tuo caso. Se hai bisogno di fagioli a molla nel quarzo, dovresti usare il supporto al quarzo fornito dalla primavera. Questo ti darà pieno accesso a tutti i bean gestiti. Per maggiori informazioni consultare la sezione quarzo ai documenti primaverili al numero http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html. Vedi anche il seguente esempio di quarzo di utilizzo con fagioli gestiti a molla. L'esempio è basato sul tuo codice. In questo modo è possibile modificare il primo frammento di codice (in cui è stata eseguita l'inizializzazione del quarzo) con alternative a molla follwoing.

Creare dettaglio lavoro in fabbrica

@Component 
public class ScheduledActionRunnerJobDetailFactory extends JobDetailFactoryBean { 

    @Autowired 
    private ScheduleService scheduleService; 

    @Override 
    public void afterPropertiesSet() { 
     setJobClass(ScheduledActionRunner.class); 
     Map<String, Object> data = new HashMap<String, Object>(); 
     data.put("scheduleService", scheduleService); 
     setJobDataAsMap(data); 
     super.afterPropertiesSet(); 
    } 
} 

Creare la fabbrica grilletto

@Component 
public class ActionCronTriggerFactoryBean extends CronTriggerFactoryBean { 

    @Autowired 
    private ScheduledActionRunnerJobDetailFactory jobDetailFactory; 

    @Value("${cron.pattern}") 
    private String pattern; 

    @Override 
    public void afterPropertiesSet() throws ParseException { 
     setCronExpression(pattern); 
     setJobDetail(jobDetailFactory.getObject()); 
     super.afterPropertiesSet(); 
    } 

} 

E infine creare lo SchedulerFactory

@Component 
public class ActionSchedulerFactoryBean extends SchedulerFactoryBean { 

    @Autowired 
    private ScheduledActionRunnerJobDetailFactory jobDetailFactory; 

    @Autowired 
    private ActionCronTriggerFactoryBean triggerFactory; 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     setJobDetails(jobDetailFactory.getObject()); 
     setTriggers(triggerFactory.getObject()); 
     super.afterPropertiesSet(); 
    } 

} 
+0

Grazie @Babl per la risposta. Prima di provare il tuo codice proposto, la mia unica e ultima domanda è: perché ha funzionato prima di cambiare l'applicazione per usare Spring Boot?Prior Spring Boot era già basato su JavaConfig e Spring 4.1, e la programmazione funzionava bene. – yglodt

+1

Come accennato in precedenza, SpringBeanAutowiringSupport utilizza il contesto dell'applicazione Web per trovare i bean e iniettarli nell'oggetto, ma poiché l'applicazione di avvio di primavera non è completamente un'applicazione Web, il contesto è nullo. Quindi, se abiliti un log per SpringBeanAutowiringSupport al livello di debug, vedrai un messaggio di sviluppo che dice che la classe di destinazione non è stata costruita in un'applicazione web Spring. Qualsiasi modo per attivare il debug leve per la primavera ti darà molte informazioni interne sul problema;) – Babl

+0

Grazie per la spiegazione dettagliata! – yglodt

9

La mia risposta non corrisponde pienamente a mettere in discussione, ma la primavera esporre un'altra possibilità: avviare lo scheduler basato su espressioni cron su qualsiasi servizio.

Utilizzando Spring.Boot è possibile configurare l'applicazione per utilizzare scheduler per semplice mettendo

@EnableScheduling 
public class Application{ 
.... 

Dopo di che è sufficiente posizionare seguente annotazione sul metodo di public (!) Di @Service

@Service 
public class MyService{ 
... 
    @Scheduled(cron = "0 * * * * MON-FRI") 
    public void myScheduledMethod(){ 
    .... 
    } 
+2

Grazie, ne sono a conoscenza e lo utilizzo già per gli orari statici. Il problema che descrivo sopra viene da Schedules che può essere impostato dall'utente, quindi ho bisogno di aggiungerli/modificarli/rimuoverli in fase di runtime, che per quanto ne so non è possibile con l'annotazione @Scheduled. – yglodt