Guida alla logica di esecuzione all'avvio in primavera

1. Introduzione

In questo articolo ci concentreremo su come eseguire la logica all'avvio di un'applicazione Spring .

2. Esecuzione della logica all'avvio

L'esecuzione della logica durante / dopo l'avvio dell'applicazione Spring è uno scenario comune, ma che causa più problemi.

Per trarre vantaggio da Inverse of Control, abbiamo naturalmente bisogno di rinunciare al controllo parziale del flusso dell'applicazione al contenitore, motivo per cui l'istanza, la logica di configurazione all'avvio, ecc. Richiedono un'attenzione particolare.

Non possiamo semplicemente includere la nostra logica nei costruttori dei bean o chiamare metodi dopo l'istanziazione di qualsiasi oggetto; semplicemente non abbiamo il controllo durante questi processi.

Diamo un'occhiata all'esempio di vita reale:

@Component public class InvalidInitExampleBean { @Autowired private Environment env; public InvalidInitExampleBean() { env.getActiveProfiles(); } }

Qui stiamo cercando di accedere a un campo autowired nel costruttore. Quando viene chiamato il costruttore, il bean Spring non è ancora completamente inizializzato. Questo è problematico perché non chiamare i campi ancora inizializzato saranno ovviamente tradurrà in NullPointerException s .

La primavera ci offre alcuni modi per gestire questa situazione.

2.1. L' annotazione @PostConstruct

L' annotazione @PostConstruct di Javax può essere utilizzata per annotare un metodo che dovrebbe essere eseguito una volta immediatamente dopo l'inizializzazione del bean . Tieni presente che il metodo annotato verrà eseguito da Spring anche se non c'è nulla da iniettare.

Ecco @PostConstruct in azione:

@Component public class PostConstructExampleBean { private static final Logger LOG = Logger.getLogger(PostConstructExampleBean.class); @Autowired private Environment environment; @PostConstruct public void init() { LOG.info(Arrays.asList(environment.getDefaultProfiles())); } }

Nell'esempio sopra puoi vedere che l' istanza di Environment è stata iniettata in modo sicuro e quindi chiamata nel metodo annotato @PostConstruct senza generare un'eccezione NullPointerException .

2.2. L' interfaccia InitializingBean

L' approccio InitializingBean funziona in modo abbastanza simile a quello precedente. Invece di annotare un metodo, è necessario implementare l' interfaccia InitializingBean e il metodo afterPropertiesSet () .

Qui puoi vedere l'esempio precedente implementato utilizzando l' interfaccia InitializingBean :

@Component public class InitializingBeanExampleBean implements InitializingBean { private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class); @Autowired private Environment environment; @Override public void afterPropertiesSet() throws Exception { LOG.info(Arrays.asList(environment.getDefaultProfiles())); } }

2.3. Un ApplicationListener

Questo approccio può essere utilizzato per eseguire la logica dopo che il contesto Spring è stato inizializzato , quindi non ci stiamo concentrando su alcun bean in particolare, ma aspettiamo che vengano inizializzati tutti.

Per ottenere ciò è necessario creare un bean che implementa l' interfaccia ApplicationListener :

@Component public class StartupApplicationListenerExample implements ApplicationListener { private static final Logger LOG = Logger.getLogger(StartupApplicationListenerExample.class); public static int counter; @Override public void onApplicationEvent(ContextRefreshedEvent event) { LOG.info("Increment counter"); counter++; } } 

Gli stessi risultati possono essere ottenuti utilizzando la nuova annotazione @EventListener :

@Component public class EventListenerExampleBean { private static final Logger LOG = Logger.getLogger(EventListenerExampleBean.class); public static int counter; @EventListener public void onApplicationEvent(ContextRefreshedEvent event) { LOG.info("Increment counter"); counter++; } }

In questo esempio abbiamo scelto ContextRefreshedEvent. Assicurati di scegliere un evento appropriato che si adatti alle tue esigenze.

2.4. Il @Bean attributo Initmethod

La proprietà initMethod può essere utilizzata per eseguire un metodo dopo l'inizializzazione di un bean.

Ecco come appare un fagiolo:

public class InitMethodExampleBean { private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class); @Autowired private Environment environment; public void init() { LOG.info(Arrays.asList(environment.getDefaultProfiles())); } }

Puoi notare che non ci sono interfacce speciali implementate né annotazioni speciali usate.

Quindi, possiamo definire il bean utilizzando l' annotazione @Bean :

@Bean(initMethod="init") public InitMethodExampleBean initMethodExampleBean() { return new InitMethodExampleBean(); }

Ed è così che appare una definizione di bean in una configurazione XML:

2.5. Iniezione del costruttore

Se stai iniettando campi usando Constructor Injection, puoi semplicemente includere la tua logica in un costruttore:

@Component public class LogicInConstructorExampleBean { private static final Logger LOG = Logger.getLogger(LogicInConstructorExampleBean.class); private final Environment environment; @Autowired public LogicInConstructorExampleBean(Environment environment) { this.environment = environment; LOG.info(Arrays.asList(environment.getDefaultProfiles())); } }

2.6. Spring Boot CommandLineRunner

Spring Boot fornisce un'interfaccia CommandLineRunner con un metodo callback run () che può essere richiamato all'avvio dell'applicazione dopo l'istanza del contesto dell'applicazione Spring.

Diamo un'occhiata a un esempio:

@Component public class CommandLineAppStartupRunner implements CommandLineRunner { private static final Logger LOG = LoggerFactory.getLogger(CommandLineAppStartupRunner.class); public static int counter; @Override public void run(String...args) throws Exception { LOG.info("Increment counter"); counter++; } }

Nota : Come indicato nella documentazione, multipli CommandLineRunner fagioli può essere definita all'interno dello stesso contesto applicativo e può essere ordinato utilizzando l' @Ordered interfaccia o @Order annotazione.

2.7. Spring Boot ApplicationRunner

Simile a CommandLineRunner, Spring boot fornisce anche un'interfaccia ApplicationRunner con un metodo run () da invocare all'avvio dell'applicazione. Tuttavia, invece di argomenti String non elaborati passati al metodo di callback, abbiamo un'istanza della classe ApplicationArguments .

The ApplicationArguments interface has methods to get argument values that are options and plain argument values. An argument that is prefixed with – – is an option argument.

Let's look at an example:

@Component public class AppStartupRunner implements ApplicationRunner { private static final Logger LOG = LoggerFactory.getLogger(AppStartupRunner.class); public static int counter; @Override public void run(ApplicationArguments args) throws Exception { LOG.info("Application started with option names : {}", args.getOptionNames()); LOG.info("Increment counter"); counter++; } }

3. Combining Mechanisms

In order to achieve full control over your beans, you might want to combine the above mechanisms together.

The order of execution is as follows:

  1. The constructor
  2. the @PostConstruct annotated methods
  3. the InitializingBean's afterPropertiesSet() method
  4. the initialization method specified as init-method in XML

Let's create a Spring bean that combines all mechanisms:

@Component @Scope(value = "prototype") public class AllStrategiesExampleBean implements InitializingBean { private static final Logger LOG = Logger.getLogger(AllStrategiesExampleBean.class); public AllStrategiesExampleBean() { LOG.info("Constructor"); } @Override public void afterPropertiesSet() throws Exception { LOG.info("InitializingBean"); } @PostConstruct public void postConstruct() { LOG.info("PostConstruct"); } public void init() { LOG.info("init-method"); } }

If you try to instantiate this bean, you will be able to see logs that match the order specified above:

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor [main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct [main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean [main] INFO o.b.startup.AllStrategiesExampleBean - init-method

4. Conclusion

In questo articolo abbiamo illustrato diversi modi di eseguire la logica all'avvio dell'applicazione di Spring.

Esempi di codice possono essere trovati su GitHub.