Eseguire il metodo all'avvio in primavera


176

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


1
qual è il trucco con @Scheduled? è esattamente quello che voglio!
chrismarx,

Risposte:


185

Se per "avvio dell'applicazione" intendi "avvio del contesto dell'applicazione", allora sì, ci sono molti modi per farlo , il più semplice (per i bean singleton, comunque) è annotare il tuo metodo @PostConstruct. Dai un'occhiata al link per vedere le altre opzioni, ma in sintesi sono:

  • Metodi annotati con @PostConstruct
  • afterPropertiesSet()come definito dall'interfaccia di InitializingBeancallback
  • Un metodo init () personalizzato configurato

Tecnicamente, questi sono agganci nel ciclo di vita del bean , piuttosto che nel ciclo di vita del contesto, ma nel 99% dei casi i due sono equivalenti.

Se devi collegarti specificamente al contesto di avvio / arresto, puoi invece implementare l' Lifecycleinterfaccia , ma probabilmente non è necessario.


7
Devo ancora vedere un'implementazione di Lifecycle o SmartLifecycle dopo parecchie ricerche. So che ha un anno, ma skaffman se hai qualcosa che puoi pubblicare sarebbe molto apprezzato.

4
I metodi di cui sopra vengono richiamati prima della creazione dell'intero contesto dell'applicazione (ad es. / Prima della demarcazione della transazione).
Hans Westerbeek,

Ricevo uno strano avvertimento nel tentativo di usare @PostConstruct in java 1.8:Access restriction: The type PostConstruct is not accessible due to restriction on required library /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar
encrest

2
Esistono casi importanti in cui il ciclo di vita di bean e contesto è molto diverso. Come notato da @HansWesterbeek, un bean può essere impostato prima che il contesto da cui dipende sia completamente pronto. Nella mia situazione un bean dipendeva da JMS - era completamente costruito, quindi il suo @PostConstructmetodo era chiamato, ma l'infrastruttura del JMS da cui dipendeva indirettamente non era ancora completamente cablata (ed essendo Spring tutto fallito silenziosamente). Al passaggio a @EventListener(ApplicationReadyEvent.class)tutto ha funzionato ( ApplicationReadyEventè Spring Boot specifico per Vanilla Spring vedi la risposta di Stefan).
George Hawkins,

@Skaffman: cosa succede se il mio bean non viene indirizzato da nessun bean e voglio inizializzarlo senza essere utilizzato da nessuna parte
Sagar Kharab,

104

Questo è fatto facilmente con un ApplicationListener. Ho ottenuto questo per lavorare ascoltando Spring 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 
  }
}

I listener di applicazioni vengono eseguiti in modo sincrono in primavera. Se vuoi assicurarti che il tuo codice venga eseguito una sola volta, mantieni uno stato nel tuo componente.

AGGIORNARE

A partire da Spring 4.2+ puoi anche usare l' @EventListenerannotazione per osservare ContextRefreshedEvent(grazie a @bphilipnyc per averlo sottolineato):

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.
Rory Hunter,

9
NB per coloro che sono tentati di usare ContextStartedEventinvece, è più difficile aggiungere l'ascoltatore prima che l'evento si attivi.
OrangeDog,

2
Come chiamare un repositopry JPA @Autowired in evento? il repository è nullo.
e-info128,

Non funziona per me. Sto usando Spring mvc 3. Questo metodo onApplicationEvent (___) non viene chiamato all'avvio dell'applicazione. Qualsiasi aiuto. Ecco il mio codice @Component classe pubblica AppStartListener implementa ApplicationListener <ContextRefreshedEvent> {public void onApplicationEvent (evento ContextRefreshedEvent finale) {System.out.println ("\ n \ n \ nInside on application event"); }}
Vishwas Tyagi il

@VishwasTyagi Come si avvia il contenitore? Sei sicuro che AppStartListener fa parte della scansione dei componenti?
Stefan Haberl,

38

In Spring 4.2+ ora puoi semplicemente fare:

@Component
class StartupHousekeeper {

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

È garantito che questo ascoltatore invochi solo una volta dopo l'avvio?
gstackoverflow

No, vedi la mia risposta sopra. Mantieni un po 'di stato nel tuo ascoltatore per verificare se si sta eseguendo per la prima volta
Stefan Haberl

13

Se si utilizza l'avvio a molla, questa è la risposta migliore.

Sento che @PostConstructe altre varie interiezioni del ciclo di vita sono modi circolari. Questi possono portare direttamente a problemi di runtime o causare difetti non ovvi a causa di eventi imprevisti del ciclo di vita del bean / contesto. Perché non semplicemente invocare direttamente il tuo bean usando Java semplice? Invoca ancora il bean in "modo primaverile" (ad es. Attraverso il proxy AoP di primavera). E soprattutto, è semplicemente Java, non può essere più semplice di così. Non sono necessari 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 si avvia il contesto dell'applicazione Spring da un test di integrazione, main non viene mai eseguito!
Jonas Geiregat,

@JonasGeiregat: Inoltre, ci sono altri scenari in cui non esiste main()affatto, ad esempio quando si utilizza un framework di applicazioni (ad esempio JavaServer Faces).
sleske,

9

Per gli utenti di Java 1.8 che stanno ricevendo un avviso quando provano a fare riferimento all'annotazione @PostConstruct, ho finito invece con il piggyback 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");
    }

}


7

Ciò che abbiamo fatto è stato estendere org.springframework.web.context.ContextLoaderListenerper stampare qualcosa quando inizia il 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 quindi in web.xml:

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

7

Con SpringBoot, possiamo eseguire un metodo all'avvio tramite @EventListenerannotazione

@Component
public class LoadDataOnStartUp
{   
    @EventListener(ApplicationReadyEvent.class)
    public void loadData()
    {
        // do something
    }
}

4

Attenzione, questo è consigliato solo se il runOnceOnStartupmetodo dipende da un contesto di primavera completamente inizializzato. Ad esempio: decidi di chiamare un dao con demarcazione delle transazioni

È inoltre possibile utilizzare un metodo pianificato con fixedDelay impostato su un valore molto alto

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

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

visto in Pianificazione delle attività da eseguire una volta, utilizzando lo spazio dei nomi delle attività di Spring


Non vedo alcun vantaggio rispetto all'uso @PostConstruct?
Wim Deblauwe,

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

5
Il metodo '@PostConstruct' di @WimDeblauwe viene attivato quando il bean viene inizializzato, l'intero contesto potrebbe non essere pronto (fe Gestione delle transazioni)
Joram

Questo è più elegante di post costruzione o di eventuali interfacce o eventi
aliopi,


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

        }
    }
}

2
ApplicationReadyEvent è in avvio a molla non in primavera 3
John Mercier,

0

Se si desidera configurare un bean prima che l'applicazione venga eseguita completamente, è possibile utilizzare @Autowired:

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

0

È possibile utilizzare @EventListeneril componente, che verrà richiamato dopo l'avvio del server e l'inizializzazione di tutti i bean.

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}

0

Per un file che si StartupHousekeeper.javatrova nel pacchetto com.app.startup,

Fallo in StartupHousekeeper.java:

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void keepHouse() {
    System.out.println("This prints at startup.");
  }
}

E farlo in myDispatcher-servlet.java:

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <mvc:annotation-driven />
    <context:component-scan base-package="com.app.startup" />

</beans>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.