2010-03-08 14 views
144

Esiste qualche funzione di Spring 3 per eseguire alcuni metodi all'avvio dell'applicazione per la prima volta? So che posso fare il trucco di impostare un metodo con l'annotazione @Scheduled e viene eseguito subito dopo l'avvio, ma poi verrà eseguito periodicamente.Esegui metodo all'avvio in primavera

Grazie.

+1

qual è il trucco con @ Programmato? è esattamente quello che voglio! – chrismarx

risposta

162

Se per "l'avvio dell'applicazione" si intende "l'avvio contesto di applicazione", allora sì, ci sono many ways to do this, il modo più semplice (per i fagioli single, comunque) è quello di annotare il metodo con @PostConstruct. Date un'occhiata al link per vedere le altre opzioni, ma in sintesi sono:

  • metodi annotati con @PostConstruct
  • afterPropertiesSet() come definito dall'interfaccia InitializingBean richiamata
  • Un init configurato personalizzato() metodo

Tecnicamente, questi sono ganci nella fagiolo ciclo di vita, piuttosto che il ciclo di vita del contesto, ma nel 99% dei casi, i due sono equivalenti.

Se è necessario collegare in modo specifico all'avvio/arresto del contesto, è possibile invece implement the Lifecycle interface, ma probabilmente non è necessario.

+6

Ho ancora visto un'implementazione di Lifecycle o SmartLifecycle dopo un bel po 'di ricerche. So che questo è un anno, ma skaffman se hai qualcosa che puoi pubblicare sarebbe molto apprezzato. –

+3

I metodi precedenti vengono richiamati prima che sia stato creato l'intero contesto dell'applicazione (ad esempio/prima/la demarcazione della transazione è stata impostata). –

+0

Viene visualizzato un avviso strano durante il tentativo di utilizzare @PostConstruct in java 1.8: 'Restrizione di accesso: il tipo PostConstruct non è accessibile a causa della restrizione sulla libreria richiesta /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar' – encrest

80

Questo è facilmente possibile con un ApplicationListener. Ho avuto questo lavoro l'ascolto di Primavera di ContextRefreshedEvent:

import org.springframework.context.ApplicationListener; 
import org.springframework.context.event.ContextRefreshedEvent; 
import org.springframework.stereotype.Component; 

@Component 
public class StartupHousekeeper implements ApplicationListener<ContextRefreshedEvent> { 

    @Override 
    public void onApplicationEvent(final ContextRefreshedEvent event) { 
    // do whatever you need here 
    } 
} 

ascoltatori applicativi eseguiti in modo sincrono in primavera. Se vuoi assicurarti che il codice venga eseguito una volta sola, mantieni uno stato nel componente.

UPDATE

partire con la primavera 4.2+ è anche possibile utilizzare il @EventListener annotazioni di osservare le ContextRefreshedEvent (grazie al @bphilipnyc per la precisazione):

import org.springframework.context.ApplicationListener; 
import org.springframework.context.event.ContextRefreshedEvent; 
import org.springframework.stereotype.Component; 

@Component 
public class StartupHousekeeper { 

    @EventListener(ContextRefreshedEvent.class) 
    public void contextRefreshedEvent() { 
    // do whatever you need here 
    } 
} 
+1

Questo ha funzionato anche per me: perfetto per l'inizializzazione non bean di una volta. –

+8

N.B. per coloro che sono tentati di usare 'ContextStartedEvent', invece, è più difficile aggiungere il listener prima che l'evento venga attivato. – OrangeDog

+1

Come chiamare un repositoryopry @Autowired JPA nell'evento? il repository è nullo. –

7

Quello che abbiamo fatto è stato che si estende org.springframework.web.context.ContextLoaderListener per stampare qualcosa all'avvio del contesto.

public class ContextLoaderListener extends org.springframework.web.context.ContextLoaderListener 
{ 
    private static final Logger logger = LoggerFactory.getLogger(ContextLoaderListener.class); 

    public ContextLoaderListener() 
    { 
     logger.info("Starting application..."); 
    } 
} 

Configurare la sottoclasse poi in web.xml:

<listener> 
    <listener-class> 
     com.mycomp.myapp.web.context.ContextLoaderListener 
    </listener-class> 
</listener> 
9

Per gli utenti di Java 1.8 che ricevono un avviso quando si tenta di fare riferimento a @PostC onstruct annotation, ho finito invece con il piggybacking dell'annotazione @Scheduled che puoi fare se hai già un lavoro @Scheduled con fixedRate o fixedDelay.

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.scheduling.annotation.EnableScheduling; 
import org.springframework.scheduling.annotation.Scheduled; 
import org.springframework.stereotype.Component; 

@EnableScheduling 
@Component 
public class ScheduledTasks { 

private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTasks.class); 

private static boolean needToRunStartupMethod = true; 

    @Scheduled(fixedRate = 3600000) 
    public void keepAlive() { 
     //log "alive" every hour for sanity checks 
     LOGGER.debug("alive"); 
     if (needToRunStartupMethod) { 
      runOnceOnlyOnStartup(); 
      needToRunStartupMethod = false; 
     } 
    } 

    public void runOnceOnlyOnStartup() { 
     LOGGER.debug("running startup job"); 
    } 

} 
+0

vedi anche http://stackoverflow.com/questions/3564361/scheduling-tasks-to-run-once-using-the-spring-task-namespace – Joram

8

Se si utilizza lo spring-boot, questa è la migliore risposta.

Ritengo che @PostConstruct e altre varie interruzioni del ciclo di vita siano modi di arrotondamento. Questi possono portare direttamente a problemi di runtime o causare meno di difetti evidenti a causa di eventi di ciclo di vita di bean/contesto imprevisti. Perché non invocare direttamente il tuo bean usando semplicemente Java? Continui a invocare il bean come "spring way" (es: attraverso il proxy AoP di primavera). E soprattutto, è semplice java, non può essere più semplice di così. Non c'è bisogno di ascoltatori di contesto o programmatori dispari.

@SpringBootApplication 
public class DemoApplication { 

    public static void main(String[] args) { 
     ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args); 

     MyBean myBean = (MyBean)app.getBean("myBean"); 

     myBean.invokeMyEntryPoint(); 
    } 
} 
+5

Questa è una buona idea, in generale, ma quando l'avvio del contesto di applicazione di primavera da un test di integrazione, main non viene mai eseguito! –

+0

@JonasGeiregat: Inoltre, ci sono altri scenari dove non c'è 'main()' a tutti, ad esempio quando si utilizza un framework di applicazioni (ad esempio JavaServer Faces). – sleske

0

Se si desidera configurare un fagiolo prima che l'applicazione è in esecuzione completamente, è possibile utilizzare @Autowired:

@Autowired 
private void configureBean(MyBean: bean) { 
    bean.setConfiguration(myConfiguration); 
} 
3

Attenzione, questo è consigliato solo se il metodo di runOnceOnStartup dipende da un completamente contesto primaverile inizializzato. Per esempio: si wan per chiamare un dao con la demarcazione delle transazioni

È inoltre possibile utilizzare un metodo programmato con FIXEDRimossa l'attesa impostato molto alto

@Scheduled(fixedDelay = Long.MAX_VALUE) 
public void runOnceOnStartup() { 
    dosomething(); 
} 

Questo ha il vantaggio che l'intera applicazione è cablata (transazioni, Dao, ...)

visti in Scheduling tasks to run once, using the Spring task namespace

+0

Non vedo alcun vantaggio rispetto all'utilizzo di '@ PostConstruct'? –

+0

@WimDeblauwe dipende da cosa si vuole fare in dosomething() chiamare un dao Autowired con demarcazione Trasaction richiede l'intero contesto da avviare, non solo questo bean – Joram

+0

puoi elaborarlo? –

0
AppStartListener implements ApplicationListener { 
    @Override 
    public void onApplicationEvent(ApplicationEvent event) { 
     if(event instanceof ApplicationReadyEvent){ 
      System.out.print("ciao"); 

     } 
    } 
} 
+0

ApplicationReadyEvent è in avvio di primavera non in primavera 3 –

25

In primavera 4.2+ ora puoi semplicemente fare:

@Component 
class StartupHousekeeper { 

    @EventListener(ContextRefreshedEvent.class) 
    void contextRefreshedEvent() { 
     //do whatever 
    } 
} 
Problemi correlati