Fiz uma pergunta geral da Spring: criei o Spring Beans automaticamente e várias pessoas responderam que ligar para o Spring ApplicationContext.getBean()
deveria ser evitado o máximo possível. Por que é que?
De que outra forma devo obter acesso aos beans que configurei o Spring para criar?
Estou usando o Spring em um aplicativo não-web e planejava acessar um ApplicationContext
objeto compartilhado, conforme descrito por LiorH .
Alteração
Aceito a resposta abaixo, mas aqui está um exemplo alternativo de Martin Fowler, que discute os méritos da injeção de dependência versus o uso de um localizador de serviço (que é essencialmente o mesmo que chamar de embrulhado ApplicationContext.getBean()
).
Em parte, Fowler declara: " Com o localizador de serviço, a classe do aplicativo solicita explicitamente [o serviço] por uma mensagem ao localizador. Com a injeção, não há solicitação explícita, o serviço aparece na classe do aplicativo - daí a inversão de controle. A inversão do controle é um recurso comum das estruturas, mas é algo que tem um preço.Tende a ser difícil de entender e leva a problemas quando você está tentando depurar.Portanto, no geral, prefiro evitá-lo [Inversão do controle ], a menos que eu precise. Isso não quer dizer que seja uma coisa ruim, só que acho que precisa justificar-se sobre a alternativa mais direta " .
new MyOtherClass()
objeto? Eu sei sobre @Autowired, mas eu só já é usado em campos e quebra emnew MyOtherClass()
..applicationContext.getBean
não é injeção de dependência: está acessando a estrutura diretamente, usando-a como localizador de serviço .Provider<Foo>
vez de umFoo
e ligandoprovider.get()
toda vez que precisar de um nova instância. Nenhuma referência ao próprio contêiner, e você pode facilmente criar umProvider
para teste.Os motivos para preferir o Localizador de Serviço em vez da Inversão de Controle (IoC) são:
O Localizador de serviço é muito, muito mais fácil para outras pessoas seguirem seu código. A IoC é 'mágica', mas os programadores de manutenção devem entender suas configurações complicadas do Spring e toda a miríade de locais para descobrir como você conectou seus objetos.
A IoC é terrível para depurar problemas de configuração. Em certas classes de aplicativos, o aplicativo não inicia quando configurado incorretamente e você pode não ter a chance de percorrer o que está acontecendo com um depurador.
A IoC é baseada principalmente em XML (as anotações melhoram as coisas, mas ainda há muito XML por aí). Isso significa que os desenvolvedores não podem trabalhar no seu programa a menos que conheçam todas as tags mágicas definidas pelo Spring. Não é bom o suficiente para conhecer Java. Isso dificulta os programadores com menos experiência (ou seja, é realmente um projeto ruim usar uma solução mais complicada quando uma solução mais simples, como o Localizador de Serviços, atende aos mesmos requisitos). Além disso, o suporte ao diagnóstico de problemas XML é muito mais fraco que o suporte a problemas Java.
A injeção de dependência é mais adequada para programas maiores. Na maioria das vezes, a complexidade adicional não vale a pena.
Frequentemente, o Spring é usado caso você "queira alterar a implementação posteriormente". Existem outras maneiras de conseguir isso sem a complexidade do Spring IoC.
Para aplicativos da Web (Java EE WARs), o contexto do Spring é efetivamente vinculado no tempo de compilação (a menos que você queira que os operadores agrupem o contexto na guerra explodida). Você pode fazer com que o Spring use arquivos de propriedades, mas com os servlets, os arquivos de propriedades precisarão estar em um local predeterminado, o que significa que você não pode implantar vários servlets ao mesmo tempo na mesma caixa. É possível usar o Spring com JNDI para alterar as propriedades no momento da inicialização do servlet, mas se você estiver usando o JNDI para parâmetros modificáveis pelo administrador, a necessidade do Spring diminui (já que o JNDI é efetivamente um Localizador de Serviço).
Com o Spring, você pode perder o programa Control se o Spring estiver despachando para seus métodos. Isso é conveniente e funciona para muitos tipos de aplicativos, mas não para todos. Você pode precisar controlar o fluxo do programa quando precisar criar tarefas (threads etc.) durante a inicialização ou precisar de recursos modificáveis que o Spring não sabia quando o conteúdo estava vinculado ao seu WAR.
O Spring é muito bom para gerenciamento de transações e tem algumas vantagens. Só que a IoC pode ter engenharia excessiva em muitas situações e introduzir complexidade injustificada para os mantenedores. Não use a IoC automaticamente sem pensar em maneiras de não usá-la primeiro.
fonte
É verdade que incluir a classe no application-context.xml evita a necessidade de usar getBean. No entanto, mesmo isso é realmente desnecessário. Se você estiver escrevendo um aplicativo independente e NÃO desejar incluir sua classe de driver no application-context.xml, poderá usar o código a seguir para que o Spring controle automaticamente as dependências do driver:
Preciso fazer isso algumas vezes quando tenho algum tipo de classe autônoma que precisa usar algum aspecto do meu aplicativo (por exemplo, para teste), mas não quero incluí-lo no contexto do aplicativo porque não é na verdade parte do aplicativo. Observe também que isso evita a necessidade de procurar o bean usando um nome String, que eu sempre achei feio.
fonte
@Autowired
anotação.Um dos benefícios mais legais de usar algo como o Spring é que você não precisa conectar seus objetos. A cabeça de Zeus se abre e suas classes aparecem, totalmente formadas com todas as dependências criadas e conectadas, conforme necessário. É mágico e fantástico.
Quanto mais você diz
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, menos mágica você recebe. Menos código é quase sempre melhor. Se a sua turma realmente precisava de um bean ClassINeed, por que você não o conectou?Dito isto, algo obviamente precisa criar o primeiro objeto. Não há nada errado com o método principal de adquirir um bean ou dois via getBean (), mas você deve evitá-lo, porque sempre que o usa, você realmente não está usando toda a magia do Spring.
fonte
A motivação é escrever um código que não dependa explicitamente do Spring. Dessa forma, se você optar por trocar de contêiner, não precisará reescrever nenhum código.
Pense no contêiner como algo invisível ao seu código, suprindo magicamente suas necessidades, sem ser solicitado.
A injeção de dependência é um contraponto ao padrão "localizador de serviço". Se você estiver procurando dependências por nome, é melhor livrar-se do contêiner de DI e usar algo como JNDI.
fonte
Usar
@Autowired
ouApplicationContext.getBean()
é realmente a mesma coisa. De ambos os modos, você obtém o bean configurado no seu contexto e, de ambos os modos, seu código depende da primavera. A única coisa que você deve evitar é instanciar seu ApplicationContext. Faça isso apenas uma vez! Em outras palavras, uma linha comodeve ser usado apenas uma vez no seu aplicativo.
fonte
A idéia é que você confie na injeção de dependência ( inversão de controle ou IoC). Ou seja, seus componentes estão configurados com os componentes de que precisam. Essas dependências são injetadas (por meio do construtor ou dos setters) - você não recebe por conta própria.
ApplicationContext.getBean()
requer que você nomeie um bean explicitamente em seu componente. Em vez disso, usando o IoC, sua configuração pode determinar qual componente será usado.Isso permite reconectar seu aplicativo com diferentes implementações de componentes com facilidade ou configurar objetos para teste de maneira direta, fornecendo variantes simuladas (por exemplo, um DAO simulado para que você não acesse um banco de dados durante o teste)
fonte
Outros apontaram para o problema geral (e são respostas válidas), mas vou apenas fazer um comentário adicional: não é que você NUNCA deva fazê-lo, mas sim o mínimo possível.
Normalmente, isso significa que é feito exatamente uma vez: durante a inicialização. E é apenas para acessar o bean "raiz", através do qual outras dependências podem ser resolvidas. Pode ser um código reutilizável, como servlet base (se estiver desenvolvendo aplicativos da web).
fonte
Uma das instalações da Spring é evitar o acoplamento . Defina e use Interfaces, DI, AOP e evite usar ApplicationContext.getBean () :-)
fonte
Uma das razões é a testabilidade. Digamos que você tenha esta classe:
Como você pode testar esse bean? Por exemplo:
Fácil né?
Enquanto você ainda depende do Spring (devido às anotações), você pode remover sua dependência do Spring sem alterar nenhum código (apenas as definições de anotação) e o desenvolvedor de teste não precisa saber nada sobre como o Spring funciona (talvez ele deva, de qualquer maneira, mas permite revisar e testar o código separadamente do que a primavera faz).
Ainda é possível fazer o mesmo ao usar o ApplicationContext. No entanto, você precisa zombar de
ApplicationContext
qual é uma interface enorme. Você precisa de uma implementação fictícia ou pode usar uma estrutura de simulação como o Mockito:Essa é uma possibilidade, mas acho que a maioria das pessoas concorda que a primeira opção é mais elegante e simplifica o teste.
A única opção que realmente é um problema é esta:
Testar isso exige grandes esforços ou seu bean tentará se conectar ao stackoverflow em cada teste. E assim que houver uma falha na rede (ou os administradores do stackoverflow o bloquearem devido à taxa de acesso excessiva), você terá testes aleatórios com falha.
Portanto, como conclusão, não diria que o uso
ApplicationContext
direto é automaticamente errado e deve ser evitado a todo custo. No entanto, se houver melhores opções (e na maioria dos casos), use as melhores opções.fonte
Eu encontrei apenas duas situações em que getBean () era necessário:
Outros mencionaram o uso de getBean () em main () para buscar o bean "main" em um programa independente.
Outro uso que fiz de getBean () está em situações em que uma configuração interativa do usuário determina a composição do bean para uma situação específica. Para que, por exemplo, parte do sistema de inicialização faça um loop através de uma tabela de banco de dados usando getBean () com uma definição de bean scope = 'prototype' e, em seguida, defina propriedades adicionais. Presumivelmente, existe uma interface do usuário que ajusta a tabela do banco de dados que seria mais amigável do que tentar (re) escrever o XML do contexto do aplicativo.
fonte
Há outro momento em que o uso do getBean faz sentido. Se você estiver reconfigurando um sistema que já existe, em que as dependências não são explicitamente chamadas nos arquivos de contexto do Spring. Você pode iniciar o processo fazendo chamadas para getBean, para não precisar conectar tudo de uma vez. Dessa forma, você pode construir lentamente sua configuração de mola, colocando cada peça no lugar ao longo do tempo e alinhando as peças corretamente. As chamadas para getBean acabarão sendo substituídas, mas, como você entende a estrutura do código, ou a falta dela, é possível iniciar o processo de conectar mais e mais beans e usar cada vez menos chamadas para getBean.
fonte
No entanto, ainda existem casos em que você precisa do padrão do localizador de serviço. por exemplo, eu tenho um bean de controlador, esse controlador pode ter alguns beans de serviço padrão, que podem ser injetados na dependência pela configuração. embora também possa haver muitos serviços adicionais ou novos que esse controlador pode chamar agora ou mais tarde, que precisa do localizador de serviço para recuperar os beans de serviço.
fonte
Você deve usar: ConfigurableApplicationContext em vez de para ApplicationContext
fonte