Nota: Esta pretende ser uma resposta canônica para um problema comum.
Eu tenho uma @Service
classe Spring ( MileageFeeCalculator
) que possui um @Autowired
campo ( rateService
), mas o campo é null
quando tento usá-lo. Os logs mostram que o MileageFeeCalculator
bean e o MileageRateService
bean estão sendo criados, mas recebo um NullPointerException
sempre que tento chamar o mileageCharge
método no meu bean de serviço. Por que o Spring não liga automaticamente o campo?
Classe do controlador:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}
Classe de serviço:
@Service
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService; // <--- should be autowired, is null
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile()); // <--- throws NPE
}
}
Bean de serviço que deve ser conectado automaticamente, MileageFeeCalculator
mas não é:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Quando tento GET /mileage/3
, recebo esta exceção:
java.lang.NullPointerException: null
at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
...
F
é chamado dentro do construtor de outro beanS
. Nesse caso, passe o bean necessárioF
como um parâmetro para o outroS
construtor de beans e anote o construtor deS
with@Autowire
. Lembre-se de anotar a classe do primeiro beanF
com@Component
.Respostas:
O campo anotado
@Autowired
énull
porque o Spring não sabe sobre a cópiaMileageFeeCalculator
que você criounew
e não sabia para conectá-la automaticamente.O contêiner Spring Inversion of Control (IoC) possui três componentes lógicos principais: um registro (chamado
ApplicationContext
) de componentes (beans) que estão disponíveis para serem usados pelo aplicativo, um sistema configurador que injeta dependências de objetos neles combinando o dependências com beans no contexto e um solucionador de dependência que pode examinar uma configuração de muitos beans diferentes e determinar como instanciar e configurá-los na ordem necessária.O contêiner de IoC não é mágico e não tem como saber sobre objetos Java, a menos que você os informe de alguma forma. Quando você liga
new
, a JVM instancia uma cópia do novo objeto e a entrega diretamente para você - ela nunca passa pelo processo de configuração. Existem três maneiras de configurar seus beans.Publiquei todo esse código, usando o Spring Boot para iniciar, neste projeto do GitHub ; você pode ver um projeto em execução completo para cada abordagem para ver tudo o que precisa para fazê-lo funcionar. Etiquete com
NullPointerException
:nonworking
Injete seus grãos
A opção mais preferível é deixar o Spring conectar automaticamente todos os seus grãos; isso requer a menor quantidade de código e é o mais sustentável. Para fazer a fiação automática funcionar como você queria, também faça a fiação automática da
MileageFeeCalculator
seguinte maneira:Se você precisar criar uma nova instância do seu objeto de serviço para pedidos diferentes, ainda poderá usar a injeção usando os escopos do bean Spring .
Etiqueta que funciona injetando o
@MileageFeeCalculator
objeto de serviço:working-inject-bean
Use @Configurable
Se você realmente precisa que os objetos criados
new
sejam conectados automaticamente, é possível usar a@Configurable
anotação Spring junto com o tecelagem em tempo de compilação AspectJ para injetar seus objetos. Essa abordagem insere código no construtor do seu objeto que alerta o Spring que ele está sendo criado para que o Spring possa configurar a nova instância. Isso requer um pouco de configuração em sua compilação (como compilar comajc
) e ativar os manipuladores de configuração de tempo de execução do Spring (@EnableSpringConfigured
com a sintaxe JavaConfig). Essa abordagem é usada pelo sistema Roo Active Record para permitir quenew
instâncias de suas entidades obtenham as informações de persistência necessárias injetadas.Tag que funciona usando
@Configurable
o objeto de serviço:working-configurable
Pesquisa manual de bean: não recomendado
Essa abordagem é adequada apenas para a interface com o código herdado em situações especiais. É quase sempre preferível criar uma classe de adaptador singleton que o Spring possa conectar automaticamente e o código legado possa chamar, mas é possível solicitar diretamente um bean ao contexto do aplicativo Spring.
Para fazer isso, você precisa de uma classe à qual o Spring possa dar uma referência ao
ApplicationContext
objeto:Em seguida, seu código legado pode chamar
getContext()
e recuperar os beans necessários:Tag que funciona pesquisando manualmente o objeto de serviço no contexto do Spring:
working-manual-lookup
fonte
@Configuration
bean, onde o método para criar uma instância de uma classe de bean específica é anotado@Bean
.@Bean
métodos ao usar o Spring Proxy AOP?@Autowired MileageFeeCalculator calc
. Alguma ideia?ApplicationContext
. Alguns usuários (para os quais fechei como duplicados) não entendem isso.Se você não estiver codificando um aplicativo Web, verifique se a classe na qual o @Autowiring é feito é um bean de primavera. Normalmente, o contêiner de mola não estará ciente da classe que poderíamos considerar um feijão de primavera. Temos que contar ao contêiner Spring sobre nossas aulas de primavera.
Isso pode ser conseguido configurando no appln-contxt ou a melhor maneira é anotar a classe como @Component e não crie a classe anotada usando o novo operador. Certifique-se de obtê-lo no contexto Appln como abaixo.
fonte
Na verdade, você deve usar objetos gerenciados pela JVM ou objetos gerenciados por Spring para chamar métodos. do código acima em sua classe de controlador, você está criando um novo objeto para chamar sua classe de serviço que possui um objeto com conexão automática.
para que não funcione dessa maneira.
A solução torna esse MileageFeeCalculator como um objeto com conexão automática no próprio controlador.
Mude sua classe de controlador como abaixo.
fonte
Certa vez, encontrei o mesmo problema quando não estava acostumado
the life in the IoC world
. O@Autowired
campo de um dos meus beans é nulo no tempo de execução.A causa raiz é que, em vez de usar o bean criado automaticamente mantido pelo contêiner Spring IoC (cujo
@Autowired
campo foiindeed
injetado corretamente), sounewing
minha própria instância desse tipo de bean e o uso. É claro que esse@Autowired
campo é nulo porque o Spring não tem chance de injetá-lo.fonte
Seu problema é novo (criação de objeto no estilo java)
Com anotação
@Service
,@Component
,@Configuration
feijão são criados nocontexto de aplicação de Primavera quando o servidor é iniciado. Mas quando criamos objetos usando o novo operador, o objeto não é registrado no contexto do aplicativo que já foi criado. Por exemplo, classe Employee.java que usei.
Veja isso:
fonte
Eu sou novo no Spring, mas descobri esta solução funcional. Por favor, diga-me se é uma maneira obsoleta.
Eu faço o Spring injetar
applicationContext
neste bean:Você pode colocar esse código também na classe principal do aplicativo, se desejar.
Outras classes podem usá-lo assim:
Dessa maneira, qualquer bean pode ser obtido por qualquer objeto no aplicativo (também intantiado com
new
) e de maneira estática .fonte
Parece ser um caso raro, mas aqui está o que aconteceu comigo:
Em
@Inject
vez disso, usamos o@Autowired
padrão javaee suportado pelo Spring. Todos os lugares funcionavam bem e os grãos eram injetados corretamente, em vez de um só lugar. A injeção de feijão parece a mesmaPor fim, descobrimos que o erro foi que nós (na verdade, o recurso de conclusão automática do Eclipse) importamos em
com.opensymphony.xwork2.Inject
vez dejavax.inject.Inject
!Então, para resumir, certifique-se de que suas anotações (
@Autowired
,@Inject
,@Service
, ...) têm pacotes corretos!fonte
Acho que você deixou de instruir a primavera a verificar as aulas com anotação.
Você pode usar
@ComponentScan("packageToScan")
na classe de configuração do seu aplicativo de mola para instruir a primavera a digitalizar.@Service, @Component
anotações etc adicionam meta descrição.O Spring injeta apenas instâncias dessas classes que são criadas como bean ou marcadas com anotação.
As classes marcadas com anotação precisam ser identificadas na primavera antes da injeção,
@ComponentScan
instrua a primavera a procurar as classes marcadas com anotação. Quando o Spring o encontra@Autowired
, procura pelo bean relacionado e injeta a instância necessária.Adicionando apenas anotações, não corrige ou facilita a injeção de dependência, o Spring precisa saber onde procurar.
fonte
<context:component-scan base-package="com.mypackage"/>
ao meubeans.xml
arquivoSe isso estiver acontecendo em uma classe de teste, verifique se você não esqueceu de anotar a classe.
Por exemplo, no Spring Boot :
Passa algum tempo ...
Spring Boot continua a evoluir . Não é mais necessário usar
@RunWith
se você usar a versão correta do JUnit .Para
@SpringBootTest
a obra ficar sozinho, você precisa uso@Test
de JUnit5 vez de JUnit4 .Se você errar nessa configuração, seus testes serão compilados, mas
@Autowired
e@Value
campos (por exemplo) seránull
. Como o Spring Boot opera por mágica, você pode ter poucos caminhos para depurar diretamente essa falha.fonte
@Value
será nulo quando usado comstatic
campos.Outra solução seria fazer uma chamada:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Para o construtor MileageFeeCalculator, como este:
fonte
ATUALIZAÇÃO: Pessoas realmente inteligentes foram rápidas em apontar isso resposta, o que explica a estranheza descrita abaixo
RESPOSTA ORIGINAL:
Não sei se isso ajuda alguém, mas fiquei com o mesmo problema, mesmo fazendo as coisas aparentemente corretas. No meu método Main, eu tenho um código como este:
e em um
token.xml
arquivo eu tive uma linhaNotei que o package.path não existe mais, então acabei de abandonar a linha para sempre.
E depois disso, o NPE começou a entrar. Em um,
pep-config.xml
eu tinha apenas 2 feijões:e a classe SomeAbac possui uma propriedade declarada como
por algum motivo desconhecido, settings é nulo em init (), quando o
<context:component-scan/>
elemento não está presente, mas quando está presente e possui alguns bs como basePackage, tudo funciona bem. Essa linha agora fica assim:e funciona. Pode ser que alguém possa fornecer uma explicação, mas para mim é o suficiente agora)
fonte
<context:component-scan/>
implica implicitamente<context:annotation-config/>
necessário para o@Autowired
trabalho.Esse é o culpado de fornecer NullPointerException.
MileageFeeCalculator calc = new MileageFeeCalculator();
Estamos usando o Spring - não é necessário criar o objeto manualmente. A criação do objeto será tratada pelo contêiner IoC.fonte
Você também pode corrigir esse problema usando a anotação @Service na classe de serviço e passando o bean classA necessário como um parâmetro para o construtor classB de outros beans e anotar o construtor da classB com @Autowired. Exemplo de trecho aqui:
fonte
O que não foi mencionado aqui está descrito neste artigo no parágrafo "Ordem de execução".
Depois de "aprender" que eu tinha que anotar uma classe com @Component ou os derivados @Service ou @Repository (acho que há mais), para conectar automaticamente outros componentes dentro deles, percebi que esses outros componentes ainda eram nulos dentro do construtor do componente pai.
O uso do @PostConstruct resolve isso:
e:
fonte
Isso é válido apenas no caso de teste de unidade.
Minha classe de serviço tinha uma anotação de serviço e era
@autowired
outra classe de componente. Quando testei, a classe de componente estava ficando nula. Porque para a classe de serviço eu estava criando o objeto usandonew
Se você estiver escrevendo teste de unidade, verifique se não está criando objetos usando
new object()
. Use injectMock.Isso corrigiu meu problema. Aqui está um link útil
fonte
Observe também que, por qualquer motivo, você fizer um método em um
@Service
asfinal
, os beans com conexão automática que você acessará a partir dele sempre serãonull
.fonte
Em palavras simples, existem principalmente duas razões para um
@Autowired
campo sernull
SUA CLASSE NÃO É UM FEIJÃO DE MOLA.
O CAMPO NÃO É UM FEIJÃO.
fonte
Não totalmente relacionado à questão, mas se a injeção de campo for nula, a injeção baseada no construtor continuará funcionando bem.
fonte