Note: This is intended to be a canonical answer for a common problem.
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) ...
Having no clue of the existence of this MyService object, Spring is not able to inject a MyComponent bean inside it. Thus, the MyComponent instance inside the MyService object we created will remain null, causing the NullPointerException we get when we try to call a method on this object.
When @Autowired doesn't work. There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.
Spring dependency injection only works with Spring-managed objects or Beans. If the object in which a Bean is getting injected is not a spring managed object, you will get null @Autowired fields. To fix this, make sure only the framework create and manage the related dependencies.
If we had used Autowired, we would have been getting the following error in the test. Therefore, it is not recommended to use Autowired. Safety — Forces Spring to provide mandatory dependencies. We make sure that the created objects are valid after construction.
The field annotated @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
: nonworking
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-bean
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-configurable
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-lookup
If you are not coding a web application, make sure your class in which @Autowiring is done is a spring bean. Typically, spring container won't be aware of the class which we might think of as a spring bean. We have to tell the Spring container about our spring classes.
This can be achieved by configuring in appln-contxt or the better way is to annotate class as @Component and please do not create the annotated class using new operator. Make sure you get it from Appln-context as below.
@Component public class MyDemo { @Autowired private MyService myService; /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("test"); ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml"); System.out.println("ctx>>"+ctx); Customer c1=null; MyDemo myDemo=ctx.getBean(MyDemo.class); System.out.println(myDemo); myDemo.callService(ctx); } public void callService(ApplicationContext ctx) { // TODO Auto-generated method stub System.out.println("---callService---"); System.out.println(myService); myService.callMydao(); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With