Executando dados de instalação na inicialização no Spring
1. Introdução
Neste artigo, vamos nos concentrar em comorun logic at the startup of a Spring application.
Leitura adicional:
Configurar um aplicativo Web de inicialização Spring
Algumas das configurações mais úteis para um aplicativo Spring Boot.
Inicialização Spring: Configurando uma Classe Principal
Aprenda a configurar a classe principal do seu aplicativo Spring Boot no Maven e Gradle.
2. Executando o Logic On Startup
Executar a lógica durante / após a inicialização do aplicativo Spring é um cenário comum, mas causa vários problemas.
A fim de nos beneficiarmos do Inverso de Controle, naturalmente precisamos renunciar ao controle parcial sobre o fluxo do aplicativo para o contêiner - é por isso que instanciação, lógica de configuração na inicialização, etc, precisam de atenção especial.
Não podemos simplesmente incluir nossa lógica nos construtores dos beans ou métodos de chamada após a instanciação de qualquer objeto; simplesmente não estamos no controle durante esses processos.
Vejamos o exemplo da vida real:
@Component
public class InvalidInitExampleBean {
@Autowired
private Environment env;
public InvalidInitExampleBean() {
env.getActiveProfiles();
}
}
Aqui, estamos tentando acessar um campoautowired no construtor. Quando o construtor é chamado, o bean Spring ainda não está totalmente inicializado. Isso é problemático porquecalling not yet initialized fields will of course result in NullPointerExceptions.
A primavera nos dá algumas maneiras de gerenciar essa situação.
2.1. A anotação@PostConstruct
A anotação@PostConstruct do Javax pode ser usada para anotar um método que deve ser executadoonce immediately after the bean’s initialization. Lembre-se de que o método anotado será executado pelo Spring, mesmo que não haja nada para injetar.
Aqui está@PostConstruct em ação:
@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()));
}
}
No exemplo acima, você pode ver que a instânciaEnvironment foi injetada com segurança e, em seguida, chamada no método anotado@PostConstruct sem lançar umNullPointerException.
2.2. A interfaceInitializingBean
A abordagemInitializingBean funciona de maneira muito semelhante à anterior. Em vez de anotar um método, você precisa implementar a interfaceInitializingBeane o métodoafterPropertiesSet().
Aqui você pode ver o exemplo anterior implementado usando a interfaceInitializingBean:
@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. AnApplicationListener
Essa abordagem pode ser usada pararunning logic after the Spring context has been initialized, portanto, não estamos nos concentrando em nenhum bean em particular, mas esperando que todos sejam inicializados.
Para conseguir isso, você precisa criar um bean que implemente a interfaceApplicationListener<ContextRefreshedEvent>:
@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++;
}
}
Os mesmos resultados podem ser obtidos usando a anotação@EventListener recém-introduzida:
@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++;
}
}
Neste exemplo, escolhemos oContextRefreshedEvent. Certifique-se de escolher um evento apropriado que atenda às suas necessidades.
2.4. O Atributo do Método Inicial@Bean
A propriedade initMethod pode ser usada para executar um método após a inicialização de um bean.
Esta é a aparência de um feijão:
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()));
}
}
Você pode notar que não há interfaces especiais implementadas nem anotações especiais usadas.
Então, podemos definir o bean usando a anotação@Bean:
@Bean(initMethod="init")
public InitMethodExampleBean exBean() {
return new InitMethodExampleBean();
}
E é assim que uma definição de bean se parece em uma configuração XML:
2.5. Injeção de Construtor
Se você estiver injetando campos usando Injeção de Construtor, você pode simplesmente incluir sua lógica em um construtor:
@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 BootCommandLineRunner
A inicialização do Spring fornece uma interfaceCommanLineRunner com um método de retorno de chamadarun() que pode ser chamado na inicialização do aplicativo após o contexto do aplicativo Spring ser instanciado.
Vejamos um exemplo:
@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++;
}
}
O código-fonte completo pode ser encontrado emGithub.
Note: conforme mencionado emdocumentation, vários beansCommandLineRunner podem ser definidos dentro do mesmo contexto de aplicativo e podem ser solicitados usando a interface@Ordered ou anotação@Order .
2.7. Spring BootApplicationRunner
Semelhante aoCommandLineRunner,, o Spring boot também fornece uma interfaceApplicationRunner com um métodorun() a ser chamado na inicialização do aplicativo. No entanto, em vez de argumentosString brutos passados para o método de retorno de chamada, temos uma instância da classeApplicationArguments.
A interfaceApplicationArguments possui métodos para obter valores de argumento que são opções e valores de argumento simples. Um argumento prefixado com - - é um argumento de opção.
Vejamos um exemplo:
@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++;
}
}
O código-fonte completo pode ser encontrado emGithub.
3. Mecanismos de combinação
Para obter controle total sobre seus grãos, convém combinar os mecanismos acima.
A ordem de execução é a seguinte:
-
O construtor
-
os métodos anotados de@PostConstruct
-
o métodoafterPropertiesSet() do InitializingBean
-
o método de inicialização especificado comoinit-method em XML
Vamos criar um bean Spring que combina todos os mecanismos:
@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");
}
}
Se você tentar instanciar esse bean, poderá ver logs que correspondem à ordem especificada acima:
[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. Conclusão
Neste artigo, ilustramos várias maneiras de executar a lógica na inicialização do aplicativo Spring.
As amostras de código podem ser encontradas emGitHub.