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.