Guía para ejecutar Logic en el inicio en Spring

1. Introducción

En este artículo nos centraremos en cómo ejecutar la lógica al inicio de una aplicación Spring .

2. Ejecución de Logic al iniciar

La ejecución de la lógica durante o después del inicio de la aplicación Spring es un escenario común, pero que causa múltiples problemas.

Para poder beneficiarnos de Inverse of Control, naturalmente necesitamos renunciar al control parcial sobre el flujo de la aplicación al contenedor, razón por la cual la creación de instancias, la lógica de configuración al inicio, etc., necesitan una atención especial.

No podemos simplemente incluir nuestra lógica en los constructores de los beans o llamar a métodos después de la instanciación de cualquier objeto; simplemente no tenemos el control durante esos procesos.

Veamos el ejemplo de la vida real:

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

Aquí, estamos intentando acceder a un campo autowired en el constructor. Cuando se llama al constructor, el bean Spring aún no está completamente inicializado. Esto es problemático porque llamar a campos aún no inicializados dará como resultado, por supuesto, NullPointerException s .

Spring nos da algunas formas de manejar esta situación.

2.1. La anotación @PostConstruct

La anotación @PostConstruct de Javax se puede usar para anotar un método que debe ejecutarse una vez inmediatamente después de la inicialización del bean . Tenga en cuenta que Spring ejecutará el método anotado incluso si no hay nada que inyectar.

Aquí está @PostConstruct en acción:

@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())); } }

En el ejemplo anterior, puede ver que la instancia de Environment se inyectó de forma segura y luego se llamó en el método anotado @PostConstruct sin lanzar una NullPointerException .

2.2. La interfaz InitializingBean

El enfoque InitializingBean funciona de manera bastante similar al anterior. En lugar de anotar un método, debe implementar la interfaz InitializingBean y el método afterPropertiesSet () .

Aquí puede ver el ejemplo anterior implementado usando la interfaz 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 oyente de aplicaciones

Este enfoque se puede usar para ejecutar la lógica después de que se haya inicializado el contexto Spring , por lo que no nos estamos enfocando en ningún bean en particular, sino esperando a que todos se inicialicen.

Para lograr esto, necesita crear un bean que implemente la interfaz 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++; } } 

Se pueden lograr los mismos resultados utilizando la anotación @EventListener recién introducida :

@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++; } }

En este ejemplo, elegimos ContextRefreshedEvent. Asegúrese de elegir un evento apropiado que se adapte a sus necesidades.

2.4. El atributo @Bean Initmethod

La propiedad initMethod se puede utilizar para ejecutar un método después de la inicialización de un bean.

Así es como se ve un frijol:

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())); } }

Puede observar que no se implementan interfaces especiales ni se utilizan anotaciones especiales.

Luego, podemos definir el bean usando la anotación @Bean :

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

Y así es como se ve una definición de bean en una configuración XML:

2.5. Inyección de constructor

Si está inyectando campos usando Constructor Injection, simplemente puede incluir su lógica en un constructor:

@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 proporciona una interfaz CommandLineRunner con un método callback run () que se puede invocar al inicio de la aplicación después de crear una instancia del contexto de la aplicación Spring.

Veamos un ejemplo:

@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 : Como se ha mencionado en la documentación, múltiples CommandLineRunner habas se puede definir dentro del mismo contexto de aplicación y se puede pedir con el @Ordered interfaz o @Order anotación.

2.7. Aplicación Spring Boot

Similar a CommandLineRunner, Spring boot también proporciona una interfaz ApplicationRunner con un método run () que se invoca al inicio de la aplicación. Sin embargo, en lugar de pasar argumentos String sin procesar al método de devolución de llamada, tenemos una instancia de la clase 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

En este artículo ilustramos múltiples formas de ejecutar la lógica en el inicio de la aplicación Spring.

Se pueden encontrar ejemplos de código en GitHub.