patternjavaspringCritical
Why is my Spring @Autowired field null?
Viewed 0 times
fieldwhyautowiredspringnull
Problem
Note: This is intended to be a canonical answer for a common problem.
I have a Spring
Controller class:
Service class:
Service bean that should be autowired in
When I try to
I have a Spring
@Service class (MileageFeeCalculator) that has an @Autowired field (rateService), but the field is null when I try to use it. The logs show that both the MileageFeeCalculator bean and the MileageRateService bean are being created, but I get a NullPointerException whenever I try to call the mileageCharge method on my service bean. Why isn't Spring autowiring the field?Controller class:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}Service class:
@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
}
}Service bean that should be autowired in
MileageFeeCalculator but it isn't:@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}When I try to
GET /mileage/3, I get this exception: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)
...Solution
The field annotated
The Spring Inversion of Control (IoC) container has three main logical components: a registry (called the
The IoC container isn't magic, and it has no way of knowing about Java objects unless you somehow inform it of them. When you call
I have posted all of this code, using Spring Boot to launch, at this GitHub project; you can look at a full running project for each approach to see everything you need to make it work. Tag with the
Inject your beans
The most preferable option is to let Spring autowire all of your beans; this requires the least amount of code and is the most maintainable. To make the autowiring work like you wanted, also autowire the
If you need to create a new instance of your service object for different requests, you can still use injection by using the Spring bean scopes.
Tag that works by injecting the
Use @Configurable
If you really need objects created with
Tag that works by using
Manual bean lookup: not recommended
This approach is suitable only for interfacing with legacy code in special situations. It is nearly always preferable to create a singleton adapter class that Spring can autowire and the legacy code can call, but it is possible to directly ask the Spring application context for a bean.
To do this, you need a class to which Spring can give a reference to the
Then your legacy code can call
Tag that works by manually looking up the service object in the Spring context:
@Autowired is null because Spring doesn't know about the copy of MileageFeeCalculator that you created with new and didn't know to autowire it.The Spring Inversion of Control (IoC) container has three main logical components: a registry (called the
ApplicationContext) of components (beans) that are available to be used by the application, a configurer system that injects objects' dependencies into them by matching up the dependencies with beans in the context, and a dependency solver that can look at a configuration of many different beans and determine how to instantiate and configure them in the necessary order.The IoC container isn't magic, and it has no way of knowing about Java objects unless you somehow inform it of them. When you call
new, the JVM instantiates a copy of the new object and hands it straight to you--it never goes through the configuration process. There are three ways that you can get your beans configured.I have posted all of this code, using Spring Boot to launch, at this GitHub project; you can look at a full running project for each approach to see everything you need to make it work. Tag with the
NullPointerException: nonworkingInject your beans
The most preferable option is to let Spring autowire all of your beans; this requires the least amount of code and is the most maintainable. To make the autowiring work like you wanted, also autowire the
MileageFeeCalculator like this:@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}If you need to create a new instance of your service object for different requests, you can still use injection by using the Spring bean scopes.
Tag that works by injecting the
@MileageFeeCalculator service object: working-inject-beanUse @Configurable
If you really need objects created with
new to be autowired, you can use the Spring @Configurable annotation along with AspectJ compile-time weaving to inject your objects. This approach inserts code into your object's constructor that alerts Spring that it's being created so that Spring can configure the new instance. This requires a bit of configuration in your build (such as compiling with ajc) and turning on Spring's runtime configuration handlers (@EnableSpringConfigured with the JavaConfig syntax). This approach is used by the Roo Active Record system to allow new instances of your entities to get the necessary persistence information injected.@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}Tag that works by using
@Configurable on the service object: working-configurableManual bean lookup: not recommended
This approach is suitable only for interfacing with legacy code in special situations. It is nearly always preferable to create a singleton adapter class that Spring can autowire and the legacy code can call, but it is possible to directly ask the Spring application context for a bean.
To do this, you need a class to which Spring can give a reference to the
ApplicationContext object:@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;
}
}Then your legacy code can call
getContext() and retrieve the beans it needs:@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 that works by manually looking up the service object in the Spring context:
working-manual-lookupCode Snippets
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}@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;
}
}@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);
}
}Context
Stack Overflow Q#19896870, score: 797
Revisions (0)
No revisions yet.