Ejecución de código después de que comience Spring Boot

211

Quiero ejecutar el código después de que mi aplicación spring-boot comience a monitorear un directorio en busca de cambios.

He intentado ejecutar un nuevo hilo pero los @Autowiredservicios no se han establecido en ese punto.

He podido encontrar ApplicationPreparedEvent, que se dispara antes de que @Autowiredse establezcan las anotaciones. Idealmente, me gustaría que el evento se active una vez que la aplicación esté lista para procesar solicitudes http.

¿Hay un mejor evento para usar, o una mejor manera de ejecutar el código después de que la aplicación esté activa en spring-boot ?

guisados
fuente
Spring boot proporciona dos interfaces, ApplicationRunner y CommandLineRunner, que se pueden usar cuando desea ejecutar el código después de que se inicia el inicio de Spring. Puede consultar este artículo para ver un ejemplo de implementación: jhooq.com/applicationrunner-spring-boot
Rahul Wagh

Respuestas:

121

Tratar:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application extends SpringBootServletInitializer {

    @SuppressWarnings("resource")
    public static void main(final String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

        context.getBean(Table.class).fillWithTestdata(); // <-- here
    }
}
Anton Bessonov
fuente
66
esto no funciona cuando implementa la aplicación como archivo war en un tomcat externo. Funciona solo con tomcat incrustado
Saurabh
No, no funciona Pero en este caso de uso, me gusta la forma más explícita en lugar de @Component. Vea la respuesta de @cjstehno para que funcione en un archivo de guerra.
Anton Bessonov
322

Es tan simple como esto:

@EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
    System.out.println("hello world, I have just started up");
}

Probado en la versión 1.5.1.RELEASE

cahen
fuente
77
gracias. Esto hizo que mi código funcionara sin ningún cambio requerido. Gracias de nuevo por una respuesta tan simple. Esto también funcionará con la anotación @RequestMapping sin ningún problema.
Harshit
16
Alguien también puede desear usarlo @EventListener(ContextRefreshedEvent.class), que se activa después de la creación del bean, pero antes de que se inicie el servidor. Se puede usar para realizar actividades antes de que cualquier solicitud llegue al servidor.
neeraj
3
@neeraj, la pregunta es sobre la ejecución del código después de que se inicie Spring Boot. Si lo usa ContextRefreshedEvent, también se ejecutará después de cada actualización.
cahen
44
probado en Spring boot 2.0.5
PUBLICACIÓN
2
Probado en la versión 2.2.2. Funciona perfectamente. Esta solución me ahorra tiempo.
Arphile
96

¿Has probado ApplicationReadyEvent?

@Component
public class ApplicationStartup 
implements ApplicationListener<ApplicationReadyEvent> {

  /**
   * This event is executed as late as conceivably possible to indicate that 
   * the application is ready to service requests.
   */
  @Override
  public void onApplicationEvent(final ApplicationReadyEvent event) {

    // here your code ...

    return;
  }
}

Código de: http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/

Esto es lo que menciona la documentación sobre los eventos de inicio:

...

Los eventos de la aplicación se envían en el siguiente orden, a medida que se ejecuta la aplicación:

Un ApplicationStartedEvent se envía al comienzo de una ejecución, pero antes de cualquier procesamiento, excepto el registro de oyentes e inicializadores.

Un ApplicationEnvironmentPreparedEvent se envía cuando se conoce el entorno que se utilizará en el contexto, pero antes de que se cree el contexto.

Se envía un ApplicationPreparedEvent justo antes de que se inicie la actualización, pero después de que se hayan cargado las definiciones de bean.

Se envía un ApplicationReadyEvent después de la actualización y se han procesado las devoluciones de llamada relacionadas para indicar que la aplicación está lista para atender las solicitudes.

Se envía un ApplicationFailedEvent si hay una excepción al inicio.

...

raspacorp
fuente
11
Como alternativa, puede hacerlo utilizando @EventListeneranotaciones en un método Bean, pasando como argumento el evento de clase al que desea conectarse.
padilo
2
Esta debería ser la respuesta elegida.
varun113
2
Esto ha cambiado en spring-boot 2. Si está portando desde 1.xy estaba usando ApplicationStartedEvent, entonces ahora quiere ApplicationStartingEvent en su lugar.
Andy Brown el
Esto funciona absolutamente bien y creo que es la mejor manera de hacerlo.
AVINASH SHRIMALI
eres el mejor
ancm
83

¿Por qué no simplemente crear un bean que inicia su monitor en la inicialización, algo como:

@Component
public class Monitor {
    @Autowired private SomeService service

    @PostConstruct
    public void init(){
        // start your monitoring in here
    }
}

el initmétodo no se llamará hasta que se realice un cableado automático para el bean.

cjstehno
fuente
14
A veces @PostConstructdispara demasiado temprano. Por ejemplo, cuando se usa Spring Cloud Stream Kafka, se @PostConstructdispara antes de que la aplicación se una a Kafka. La solución de Dave Syer es mejor porque se dispara a tiempo.
Elnur Abdurrakhimov
9
@PostConstructsucede durante la inicialización, no después. Aunque esto puede ser útil en algunos casos, no es la respuesta correcta si desea ejecutar después del inicio de Spring Boot. Por ejemplo, mientras @PostConstructno termina, ninguno de los puntos finales está disponible.
cahen
63

La forma de "Spring Boot" es usar a CommandLineRunner. Solo agregue frijoles de ese tipo y estará listo. En Spring 4.1 (Boot 1.2) también hay un SmartInitializingBeanque recibe una devolución de llamada después de que todo se haya inicializado. Y la hay SmartLifecycle(desde la primavera 3).

Dave Syer
fuente
¿Algún ejemplo de eso? ¿Es posible ejecutar un bean después de que la aplicación se esté ejecutando, a través de la línea de comandos en un momento arbitrario?
Emilio
55
No sé a qué te refieres con "momento arbitrario". La guía del usuario y los ejemplos de Spring Boot tienen ejemplos del uso de CommandLineRunner(y el más reciente ApplicationRunner): docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/… .
Dave Syer
Descubrí que Lifecycle es la opción preferida para realizar tareas asincrónicas en las etapas de inicio / detención de la aplicación, y estoy tratando de detectar otras diferencias entre CommandLineRunner y InitializingBeans, pero no puedo encontrar nada al respecto.
Saljuama
3
par código de ejemplo habitual de usoCommandLineRunner
Alexey Simonov
41

Puede extender una clase usando ApplicationRunner, anular el run()método y agregar el código allí.

import org.springframework.boot.ApplicationRunner;

@Component
public class ServerInitializer implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {

        //code goes here

    }
}
Gimhani
fuente
Perfecto en Spring Boot. Pero el método run () se llamó dos veces al tener ApplicationScope para la clase. Entonces, el método PostConstruct con lo anterior funcionó mejor.
Sam
26

ApplicationReadyEventen realidad solo es útil si la tarea que desea realizar no es un requisito para el correcto funcionamiento del servidor. Iniciar una tarea asincrónica para monitorear algo en busca de cambios es un buen ejemplo.

Sin embargo, si su servidor está en un estado 'no listo' hasta que se complete la tarea, entonces es mejor implementarlo SmartInitializingSingletonporque recibirá la devolución de llamada antes de que su puerto REST se haya abierto y su servidor esté abierto para los negocios.

No caigas en la tentación de utilizarlo @PostConstructpara tareas que solo deberían ocurrir una vez. Obtendrá una sorpresa grosera cuando note que se llama varias veces ...

Andy Brown
fuente
Esta debería ser la respuesta elegida. Como señala @Andy, se llama a SmartInitializingSingleton justo antes de que se abran los puertos.
Srikanth
25

Con configuración de resorte:

@Configuration
public class ProjectConfiguration {
    private static final Logger log = 
   LoggerFactory.getLogger(ProjectConfiguration.class);

   @EventListener(ApplicationReadyEvent.class)
   public void doSomethingAfterStartup() {
    log.info("hello world, I have just started up");
  }
}
freemanpolys
fuente
12

Usa un SmartInitializingSingletonfrijol en primavera> 4.1

@Bean
public SmartInitializingSingleton importProcessor() {
    return () -> {
        doStuff();
    };

}

Como alternativa, CommandLineRunnerse puede implementar un bean o anotar un método de bean con @PostConstruct.

Jeff
fuente
¿Puedo requerir una dependencia de Autowired dentro de ese método? Me gustaría establecer perfiles
LppEdd
7

Proporcionando un ejemplo para la respuesta de Dave Syer, que funcionó a las mil maravillas:

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

    @Override
    public void run(String...args) throws Exception {
        logger.info("Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.", Arrays.toString(args));
    }
}
Paulo Pedroso
fuente
7

Pruebe este y ejecutará su código cuando el contexto de la aplicación se haya iniciado por completo.

 @Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent arg0) {
                // EXECUTE YOUR CODE HERE 
    }
}
Kalifornium
fuente
6

Realmente me gusta la sugerencia para el uso de la EventListeneranotación por @cahen ( https://stackoverflow.com/a/44923402/9122660 ) ya que está muy limpia. Lamentablemente, no pude hacer que esto funcionara en una configuración Spring + Kotlin. Lo que sí funciona para Kotlin es agregar la clase como parámetro de método:

@EventListener 
fun doSomethingAfterStartup(event: ApplicationReadyEvent) {
    System.out.println("hello world, I have just started up");
}
Ostecke
fuente
Póngalo en la clase de aplicación de arranque de primavera no al azar fuera@SpringBootApplication class MyApplication { @EventListener(ApplicationReadyEvent::class) fun doSomethingAfterStartup() { println("hello world, I have just started up") } }
Ahmed na
no necesita ponerlo a la clase @SpringBootApplication. cualquier clase de configuración servirá
George Marin
5

La mejor manera de ejecutar el bloque de código después de que se inicie la aplicación Spring Boot es usar la anotación PostConstruct, o también puede usar el corredor de línea de comando para el mismo.

1. Usando la anotación PostConstruct

@Configuration
public class InitialDataConfiguration {

    @PostConstruct
    public void postConstruct() {
        System.out.println("Started after Spring boot application !");
    }

}

2. Usando la línea de comando bean runner

@Configuration
public class InitialDataConfiguration {

    @Bean
    CommandLineRunner runner() {
        return args -> {
            System.out.println("CommandLineRunner running in the UnsplashApplication class...");
        };
    }
}
Naresh Bhadke
fuente
3

simplemente implemente CommandLineRunner para la aplicación de arranque de primavera. Necesita implementar el método de ejecución,

public classs SpringBootApplication implements CommandLineRunner{

    @Override
        public void run(String... arg0) throws Exception {
        // write your logic here 

        }
}
prasad kp
fuente
0

La mejor manera de usar CommandLineRunner o ApplicationRunner La única diferencia entre el método run () CommandLineRunner acepta una matriz de cadenas y ApplicationRunner acepta ApplicationArugument.

Rushi Vanga
fuente