Código em execução após o início do Spring Boot


211

Quero executar o código depois que meu aplicativo de inicialização por primavera começar a monitorar um diretório em busca de alterações.

Eu tentei executar um novo thread, mas os @Autowiredserviços não foram definidos nesse ponto.

Eu consegui encontrar o ApplicationPreparedEventque é acionado antes que as @Autowiredanotações sejam definidas. Idealmente, eu gostaria que o evento fosse acionado assim que o aplicativo estiver pronto para processar solicitações http.

Existe um evento melhor para usar ou uma maneira melhor de executar código depois que o aplicativo estiver ativo na inicialização por mola ?



A inicialização por mola fornece duas interfaces ApplicationRunner e CommandLineRunner, que podem ser usadas quando você deseja executar o código após o início da inicialização. Você pode consultar este artigo para obter um exemplo de implementação - jhooq.com/applicationrunner-spring-boot
— Rahul Wagh

Respostas:


121

Experimentar:

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

6
isso não funciona quando você implementa o aplicativo como arquivo war em um tomcat externo. Ele funciona apenas com tomcat incorporado
— Saurabh

Não, não funciona. Mas, neste caso de uso, gosto de maneira mais explícita, em vez de @Component. Veja a resposta de @cjstehno para que ele funcione em um arquivo de guerra.
— Anton Bessonov 30/06

321

É tão simples quanto isto:

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

Testado na versão 1.5.1.RELEASE


7
Te agradece. isso fez meu código funcionar sem nenhuma alteração necessária. Obrigado novamente por uma resposta tão simples. Isso também funcionará com a anotação @RequestMapping sem nenhum problema.
— Harshit

16
Alguém também pode querer usar @EventListener(ContextRefreshedEvent.class), que é acionado após a criação do bean, mas antes do servidor iniciar. Pode ser usado para executar atividades antes que qualquer solicitação atinja o servidor.
— neeraj

3
@neeraj, a questão é sobre a execução de código após o início do Spring Boot. Se você usar ContextRefreshedEvent, ele será executado após cada atualização também.
— cahen

4
testado no Spring boot 2.0.5.RELEASE
— ritesh 31/07/19

2
Testado na versão 2.2.2. funciona perfeitamente. Esta solução economiza meu tempo.
— Arphile

96

Você já tentou o 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/

Isto é o que a documentação menciona sobre os eventos de inicialização:

...

Os eventos do aplicativo são enviados na seguinte ordem, conforme seu aplicativo é executado:

Um ApplicationStartedEvent é enviado no início de uma execução, mas antes de qualquer processamento, exceto o registro de ouvintes e inicializadores.

Um ApplicationEnvironmentPreparedEvent é enviado quando o ambiente a ser usado no contexto é conhecido, mas antes que o contexto seja criado.

Um ApplicationPreparedEvent é enviado imediatamente antes do início da atualização, mas após o carregamento das definições do bean.

Um ApplicationReadyEvent é enviado após a atualização e todos os retornos de chamada relacionados foram processados ​​para indicar que o aplicativo está pronto para atender às solicitações.

Um ApplicationFailedEvent é enviado se houver uma exceção na inicialização.

...


11
Como alternativa, você pode fazer isso usando a @EventListeneranotação no método Bean, passando como argumento o evento de classe ao qual deseja conectar.
— padilo

2
Essa deve ser a resposta escolhida.
— varun113

2
Isso mudou no spring-boot 2. Se você está migrando do 1.x e estava usando ApplicationStartedEvent, agora deseja o ApplicationStartingEvent.
— Andy Brown

Isso funciona absolutamente bem e acho a melhor maneira de fazer.
— AVINASH SHRIMALI

você é o melhor
— ancm 21/01/19

83

Por que não criar um bean que inicia o monitor na inicialização, algo como:

@Component
public class Monitor {
    @Autowired private SomeService service

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

o initmétodo não será chamado até que qualquer ligação automática seja feita para o bean.


14
Às vezes, é @PostConstructacionado muito cedo. Por exemplo, ao usar o Spring Cloud Stream Kafka, é @PostConstructacionado antes que o aplicativo seja vinculado ao Kafka. A solução de Dave Syer é melhor porque é acionada em tempo hábil.
— Elnur Abdurrakhimov 23/04

9
@PostConstructacontece durante a inicialização, não depois. Embora isso possa ser útil em alguns casos, não é a resposta correta se você deseja executar após o início do Spring Boot. Por exemplo, embora @PostConstructnão seja concluído, nenhum dos pontos de extremidade está disponível.
— cahen

63

A maneira "Spring Boot" é usar a CommandLineRunner. Basta adicionar grãos desse tipo e você estará pronto. No Spring 4.1 (Boot 1.2), há também um SmartInitializingBeanque recebe um retorno de chamada depois que tudo foi inicializado. E existe SmartLifecycle(da Primavera 3).


Algum exemplo disso? É possível executar um bean após a execução do aplicativo, via linha de comando, em um momento arbitrário?
— Emilio

5
Não sei o que você quer dizer com "momento arbitrário". O guia do usuário e as amostras do Spring Boot têm exemplos de uso de um CommandLineRunner(e mais recente ApplicationRunner): docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/… .
— Dave Syer

Descobri que o Ciclo de Vida é a opção preferida para executar tarefas assíncronas nos estágios de início / parada do aplicativo, e estou tentando identificar outras diferenças entre o CommandLineRunner e o InitializingBeans, mas não consigo encontrar nada sobre isso.
— saljuama

3
par habitual código exemplo do uso deCommandLineRunner
— Alexey Simonov

41

Você pode estender uma classe usando ApplicationRunner, substituir o run()método e adicionar o código lá.

import org.springframework.boot.ApplicationRunner;

@Component
public class ServerInitializer implements ApplicationRunner {

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

        //code goes here

    }
}

Perfeito em Bota de Primavera. Mas o método run () foi chamado duas vezes ao ter ApplicationScope para a classe. Portanto, o método PostConstruct com o descrito acima funcionou melhor.
— Sam

26

ApplicationReadyEventé realmente útil apenas se a tarefa que você deseja executar não for um requisito para a operação correta do servidor. Iniciar uma tarefa assíncrona para monitorar algo em busca de alterações é um bom exemplo.

Se, no entanto, seu servidor estiver em um estado 'não pronto' até que a tarefa seja concluída, é melhor implementá- SmartInitializingSingletonlo, pois você receberá o retorno de chamada antes que sua porta REST seja aberta e seu servidor esteja aberto para negócios.

Não fique tentado a usar @PostConstructtarefas que só devem acontecer uma vez. Você receberá uma surpresa rude quando perceber que ele é chamado várias vezes ...


Essa deve ser a resposta escolhida. Como aponta @Andy, SmartInitializingSingleton é chamado antes das portas serem abertas.
— precisa

24

Com configuração de mola:

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

12

Use um SmartInitializingSingletonfeijão na primavera> 4.1

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

}

Como alternativa, um CommandLineRunnerbean pode ser implementado ou anotando um método de bean @PostConstruct.


Posso exigir uma dependência com conexão automática dentro desse método? Gostaria de definir perfis
— LppEdd 30/01/19

7

Fornecendo um exemplo para a resposta de Dave Syer, que funcionou como um encanto:

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

7

Experimente este e ele executará seu código quando o contexto do aplicativo for totalmente iniciado.

 @Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {

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

6

Eu realmente gosto da sugestão de uso da EventListeneranotação por @cahen ( https://stackoverflow.com/a/44923402/9122660 ), pois ela é muito limpa. Infelizmente, não consegui fazer isso funcionar em uma configuração do Spring + Kotlin. O que funciona para o Kotlin é adicionar a classe como um parâmetro do método:

@EventListener 
fun doSomethingAfterStartup(event: ApplicationReadyEvent) {
    System.out.println("hello world, I have just started up");
}

Colocá-lo na classe de aplicativo de inicialização de primavera não aleatoriamente do lado de fora@SpringBootApplication class MyApplication { @EventListener(ApplicationReadyEvent::class) fun doSomethingAfterStartup() { println("hello world, I have just started up") } }
— Ahmed na

você não precisa colocá-lo necessário na classe @SpringBootApplication. qualquer classe de configuração fará
— George Marin

5

A melhor maneira de executar o bloco de código após o início do aplicativo Spring Boot é usar a anotação PostConstruct ou também usar o corredor de linha de comando para o mesmo.

1. Usando a anotação PostConstruct

@Configuration
public class InitialDataConfiguration {

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

}

2. Usando Bean Runner da Linha de Comandos

@Configuration
public class InitialDataConfiguration {

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

3

basta implementar CommandLineRunner para o aplicativo de inicialização por primavera. Você precisa implementar o método run,

public classs SpringBootApplication implements CommandLineRunner{

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

        }
}

0

Melhor maneira de usar CommandLineRunner ou ApplicationRunner A única diferença entre o método run () CommandLineRunner aceita matriz de cadeias e ApplicationRunner aceita ApplicationArugument.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.