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 ?

guisados
fonte
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
    }
}
Anton Bessonov
fonte
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

cahen
fonte
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.

...

raspacorp
fonte
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.

cjstehno
fonte
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).

Dave Syer
fonte
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

    }
}
Gimhani
fonte
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 ...

Andy Brown
fonte
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");
  }
}
freemanpolys
fonte
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.

Jeff
fonte
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));
    }
}
Paulo Pedroso
fonte
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 
    }
}
Kalifornium
fonte
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");
}
Ostecke
fonte
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...");
        };
    }
}
Naresh Bhadke
fonte
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 

        }
}
prasad kp
fonte
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.

Rushi Vanga
fonte