O campo anotado @Autowired
é null
porque o Spring não sabe sobre a cópia MileageFeeCalculator
que você criou new
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:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
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 com ajc
) 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 que new
instâncias de suas entidades obtenham as informações de persistência necessárias injetadas.
@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
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:
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
Em seguida, seu código legado pode chamar getContext()
e recuperar os beans necessários:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
return calc.mileageCharge(miles);
}
}
Tag que funciona pesquisando manualmente o objeto de serviço no contexto do Spring: working-manual-lookup
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
.