Why is my Spring @Autowired field null?

2020-01-22 10:44发布

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)
    ...

17条回答
趁早两清
2楼-- · 2020-01-22 10:57

What hasn't been mentioned here is described in this article in the paragraph "Order of execution".

After "learning" that I had to annotate a class with @Component or the derivatives @Service or @Repository (I guess there are more), to autowire other components inside them, it struck me that these other components still were null inside the constructor of the parent component.

Using @PostConstruct solves that:

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}

and:

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

    public MyComponent() {
        // dao is null here
    }

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}
查看更多
做个烂人
3楼-- · 2020-01-22 10:57

In simple words there are mainly two reasons for an @Autowired field to be null

  • YOUR CLASS IS NOT A SPRING BEAN.

  • THE FIELD IS NOT A BEAN.

查看更多
唯我独甜
4楼-- · 2020-01-22 10:59

Also note that if, for whatever reason, you make a method in a @Service as final, the autowired beans you will access from it will always be null.

查看更多
放荡不羁爱自由
5楼-- · 2020-01-22 11:08

It seems to be rare case but here is what happened to me:

We used @Inject instead of @Autowired which is javaee standard supported by Spring. Every places it worked fine and the beans injected correctly, instead of one place. The bean injection seems the same

@Inject
Calculator myCalculator

At last we found that the error was that we (actually, the Eclipse auto complete feature) imported com.opensymphony.xwork2.Inject instead of javax.inject.Inject !

So to summarize, make sure that your annotations (@Autowired, @Inject, @Service ,... ) have correct packages!

查看更多
成全新的幸福
6楼-- · 2020-01-22 11:09

Another solution would be putting call: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
To MileageFeeCalculator constructor like this:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); 
    }
}
查看更多
Melony?
7楼-- · 2020-01-22 11:10

If this is happening in a test class, make sure you haven't forgotten to annotate the class.

For example, in Spring Boot:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
查看更多
登录 后发表回答