Boa tarde, Khabrovites!
Neste artigo, proponho discutir um dos problemas frequentemente encontrados em projetos usando a estrutura Spring. O problema descrito neste artigo surge devido a um dos erros típicos nas configurações de mola. Não é necessário tentar cometer esse erro na configuração e, portanto, esse erro é bastante comum.
Declaração do problema
O problema apresentado neste artigo está relacionado à configuração incorreta de beans no contexto atual do aplicativo, retirados de outros contextos do aplicativo. Esse problema pode surgir em uma grande aplicação industrial, que consiste em muitos jarros, cada um com seu próprio contexto de aplicação contendo beans de primavera.
Como resultado da configuração incorreta, obtemos várias cópias de beans com um estado imprevisível, mesmo se eles tiverem o escopo único. Além disso, a cópia impensada de beans pode levar ao fato de que mais de uma dúzia de cópias de todos os beans de qualquer jar serão criadas no aplicativo, o que está repleto de problemas de desempenho do aplicativo e um aumento no tempo de inicialização do aplicativo.
Um exemplo de uso de um bean de um contexto de aplicativo externo no atual
Imagine que estamos desenvolvendo um dos módulos de aplicativos, no qual existem muitos outros módulos, e que cada um deles possui seu próprio contexto de aplicativos. Esse aplicativo deve ter um módulo no qual as instâncias do contexto do aplicativo de todos os módulos do aplicativo são criadas.

Suponha que, no contexto de aplicação de um dos módulos externos, seja criada uma instância do bean da classe NumberGenerator, que queremos obter em nosso módulo. Suponha também que a classe NumberGenerator esteja localizada no pacote org.example.kruchon.generators, que armazena algumas classes que geram valores.

Este bean possui um estado - o campo int count.
package org.example.kruchon.calculators public class NumberGenerator { private int count = 0; public synchronized int next() { return count++; } }
Uma instância desse bean é criada na subconfiguração GeneratorsConfiguration.
@Configuration public class GeneratorsConfiguration { @Bean public NumberGenerator numberGenerator() { return new NumberGenerator(); } ... }
Também no contexto de aplicativo externo, há uma configuração principal na qual todas as subconfigurações do módulo externo são importadas.
@Configuration @Import({GeneratorsConfiguration.class, ...}) public class ExternalContextConfiguration { ... }
Agora, darei alguns exemplos nos quais o bean singleton da classe NumberGenerator está configurado incorretamente na configuração do contexto atual do aplicativo.
Configuração incorreta 1. Importando a configuração principal do contexto de aplicativo externo
A pior decisão que poderia ser.
@Configuration @Import(ExternalContextConfiguration.class) public class CurrentContextConfiguration { ... }
- O aplicativo recria todas as instâncias de beans do contexto de aplicativo externo. Em outras palavras, é criada uma cópia de todo o módulo externo, que afeta o consumo de memória, desempenho e tempo de inicialização do aplicativo.
- Obtenha uma cópia do NumberGenerator no contexto atual do aplicativo. Uma cópia do NumberGenerator tem seu próprio valor para o campo count, inconsistente com a primeira instância do NumberGenerator. Essa inconsistência gera erros difíceis de depurar no aplicativo.
Configuração incorreta 2. Importar subconfiguração do contexto de aplicativo externo
A segunda opção está incorreta e frequentemente encontrada na prática.
@Configuration @Import(GeneratorsConfiguration.class) public class CurrentContextConfiguration { ... }
Nesta modalidade, uma cópia completa do módulo externo não é mais criada, no entanto, obtemos novamente o segundo bean da classe NumberGenerator.
Configuração incorreta 3. Procure a injeção diretamente no bean, onde queremos usar o NumberGenerator
public class OrderFactory { private final NumberGenerator numberGenerator; public OrderFactory() { ApplicationContext externalApplicationContext = getExternalContext(); numberGenerator = externalApplicationContext.getBean(NumberGenerator.class); } public Order create() { Order order = new Order(); int id = numberGenerator.next(); order.setId(id); order.setCreatedDate(new Date()); return order; } private ApplicationContext getExternalContext(){ ... } }
Nesse método, a duplicação de um bean com o escopo singleton pode ser considerada resolvida. De fato, agora reutilizamos o bean de outro contexto de aplicativo e não o recriamos!
Mas assim:
- Complica a classe desenvolvida e seu teste de unidade.
- Exclui a implementação automática do bean da classe NumberGenerator nos beans do módulo atual.
- Não é habitual usar lookUp para injetar um bean singleton em casos gerais.
Portanto, essa solução é mais uma solução desajeitada do que uma solução racional para um problema.
Considere como configurar corretamente um bean a partir de um contexto de aplicativo externo.
Solução 1. Obtenha o bean do contexto de aplicativo externo na configuração
Este método é muito semelhante ao terceiro exemplo de configuração incorreta, com uma diferença: obtemos um bean fazendo uma pesquisa a partir do contexto externo na configuração, e não diretamente no bean.
@Configuration public class CurrentContextConfiguration { @Bean public NumberGenerator numberGenerator() { ApplicationContext externalApplicationContext = getExternalContext(); return externalApplicationContext.getBean(NumberGenerator.class); } private ApplicationContext getExternalContext(){ ... } }
Agora podemos incorporar automaticamente esse bean nos beans a partir de nosso próprio módulo.
Solução 2. Torne o contexto do aplicativo externo pai
É provável que a funcionalidade do módulo atual estenda a funcionalidade do externo. Pode haver um caso em que em um dos módulos externos são desenvolvidos beans auxiliares comuns a todo o aplicativo e em outros módulos esses beans são utilizados. Nesse caso, é lógico indicar que o módulo externo é pai do módulo anterior. Nesse caso, todo o bean do módulo pai pode ser usado no módulo atual e, em seguida, o
bean do módulo pai não precisa ser configurado na configuração do contexto atual do aplicativo.
É possível especificar um relacionamento pai ao criar uma instância de contexto usando o construtor com o parâmetro pai:
public AbstractApplicationContext(ApplicationContext parent) { ... }
Ou use o setter:
public void setParent(ApplicationContext parent) { ... }
Se o contexto do aplicativo for declarado em xml, podemos usar o construtor:
public ClassPathXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException { ... }
Conclusão
Portanto, tenha cuidado ao configurar o spring beans, siga as recomendações fornecidas no artigo e tente não copiar os beans com escopo único. Terei o maior prazer em responder às suas perguntas!