简体   繁体   中英

Error creating bean while using Autowired

I have a simple Springboot application in which I am trying to use in memory HashMap as a DB to store some info. This is my restcontroler

    @RestController
public class Parking {

    @Autowired
    ParkingServices parkingServices;

    @Autowired
    Vehicle vehicle;

    @GetMapping("/test")
    public void park() {
        vehicle = Vehicle.builder().color("Blue").plateNumber("qwew").build();
        parkingServices.parkCar(vehicle);
    }

    @GetMapping("/")
    public void Test() {
        System.out.println("test");
    }
}

This is my simple parking service

   @Service
public class ParkingServices {

    @Autowired
    ParkingLotService parkingLot;

    public ParkingServices() {
        parkingLot.init(10);
    }

    public int parkCar(Vehicle vehicle) {
        return parkingLot.parkCar(vehicle);
    }
}

And this is my ParkingLot service

  @Service
public class ParkingLotService {
    private int size = 0;
    private HashMap<Integer, Slot> parkingSlots = new HashMap<>();
    private Stack<Integer> st = new Stack<>();

    public void init(int size) {
        this.size = size;
        parkingSlots = new HashMap<>();
        for (int i = 0; i < size; i++) {
            parkingSlots.put(i, new ParkingSlot());
            st.push(i);
        }
    }

    public int parkCar(Vehicle vehicle) {
        int firstPark = st.pop();
        Slot slot = parkingSlots.get(firstPark);
        slot.parkVehicle(vehicle);
        parkingSlots.put(firstPark, slot);
        return firstPark;
    }

    public void removeCar(int parkingNumber) {
        Slot slot = parkingSlots.get(parkingNumber);

    }
}

Now when i try to run the application I am getting this error as a stack trace

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'parking': Unsatisfied dependency expressed through field 'parkingServices'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'parkingServices' defined in file [D:\Projects\parking\demo\target\classes\com\parking\demo\Services\ParkingServices.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.parking.demo.Services.ParkingServices]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.5.jar:5.3.5]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.5.jar:5.3.5]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.4.jar:2.4.4]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:769) [spring-boot-2.4.4.jar:2.4.4]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761) [spring-boot-2.4.4.jar:2.4.4]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426) [spring-boot-2.4.4.jar:2.4.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:326) [spring-boot-2.4.4.jar:2.4.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1313) [spring-boot-2.4.4.jar:2.4.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) [spring-boot-2.4.4.jar:2.4.4]
    at com.parking.demo.ParkingApplication.main(ParkingApplication.java:11) [classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'parkingServices' defined in file [D:\Projects\parking\demo\target\classes\com\parking\demo\Services\ParkingServices.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.parking.demo.Services.ParkingServices]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1316) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ~[spring-beans-5.3.5.jar:5.3.5]
    ... 21 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.parking.demo.Services.ParkingServices]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:225) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) ~[spring-beans-5.3.5.jar:5.3.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1308) ~[spring-beans-5.3.5.jar:5.3.5]
    ... 32 common frames omitted
Caused by: java.lang.NullPointerException: null
    at com.parking.demo.Services.ParkingServices.<init>(ParkingServices.java:15) ~[classes/:na]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_252]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_252]
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_252]
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_252]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:212) ~[spring-beans-5.3.5.jar:5.3.5]
    ... 34 common frames omitted

I know its due to Autowired is not working on ParkingLotService, its ability to work if instead of Autowired in ParkingService, I directly try to initialize Object but not the other way around. Can someone help me with it? Why Autowired is not working and how to make it work.

Spring @Autowired field injection after construction invoked.

See:

Autowired Fields
Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public.

Spring Autowired

So for your scenario, you can use:

  1. Autowired Constructors to inject ParkingLotService and invoke the init method.
    @Autowired
    public ParkingServices(ParkingLotService parkingLot) {
        parkingLot.init(10);
        this.parkingLot = parkingLot;
    }
  1. use @PostConstruct method to invoke the init method
@PostConstruct
public void myInit() {
  parkingLot.init(10);
}

TL;DR: Use Constructor-based DI

  1. move the @Autowired atop constructor
  2. add field as parameter to constructor and assign therein first

Error Analysed

You got this UnsatisfiedDependencyException because:

Error creating bean with name 'parking':

This bean could not be created because:

Unsatisfied dependency expressed through field 'parkingServices';

So the field could not be autowired by Spring with a dependency because of a NullPointerException (NPE) :

nested exception is org.springframework.beans.factory.BeanCreationException: Constructor threw exception; nested exception is java.lang.NullPointerException


Continue to answer explaining M. Deinum comment below :

The problem is the accessing of a variable before it is available.


The NPE was raised when the constructor of ParkingServices tries to call init on the field parkingLot , because the field was null .

@Autowired
ParkingLotService parkingLot; // field autowired in 2nd step

public ParkingServices() {
    parkingLot.init(10);      // constructor called in 1st step, hence NPE
}

Spring's @Autowired Explained

Spring's @Autowired is a Java-annotation to facilitate Dependency-Injection (DI).

Spring as most DI-frameworks offers 3 different ways of DI:

  1. Constructor-based DI
  2. Field-based DI
  3. Setter-based DI

In your case you use Field-based DI. This fails, because Spring first calls the constructor before autowiring fields (= injecting beans into Autowired annotated fields).

See the tutorial of LogicBig (2020): Spring - Different ways of injecting dependencies It shows a nice illustration: 不同的 DI 形式

Vicious circle of dependencies

Your constructor fails with a NPE, because it calls a method on a null field. The field is null because it was not yet instantiated, ie autowired . And it will not unless the constructor succeeds.

Solution recommended

Use Constructor-based DI by adding the field as parameter to the constructor and moving the @Autowired annotation form field to constructor.

ParkingLotService parkingLot; // field autowired by constructor in 1nd step

@Autowired   // tells Spring to inject suitable beans for parameters
public ParkingServices(ParkingLotService parkingLot) {  // add field as required dependency
    this.parkingLot = parkingLot;  // instantiate field
    this.parkingLot.init(10);      // call init on non-null field, avoid NPE
}

Benefits

This way you can instantiate and call init methods on the field in the same code block. So related code is near in scope, near to understand, near to maintain.

See the comparison of DI-methods and their benefis in Rizvi's Blog: Different Types of Bean Injection in Spring :

场- VS 构造函数-注入

Bonus Review

As M. Deinum commented on your question:

Calling init is btw a bad idea, because if you use this in multiple classes this will lead to issues . The service is a singleton shared between those classes...

(bold emphasis by me)

This side-effect could be solved by "initializing" the ParkingLotService right away in its constructor, like:

public ParkingLotService(int size) {  // former init
    this.size = size;
    this.parkingSlots = new HashMap<>();
    for (int i = 0; i < size; i++) {
        this.parkingSlots.put(i, new ParkingSlot());
        this.freeParkStack.push(i);     // renamed `st` to `freeParkStack`
    }
}

This way your singleton is "initialized" once, guaranteed and mandatory (because done with constructor, nobody will forget to init it). If a re-initialisation (here resizing) is still needed, the code can be extracted into a separate method later.

General advices:

  • always name your variables to express purpose (avoid abbreviations)
  • prefix a classes fields with this. to distinguish them from parameters (also avoids compiler errors when same names assigned)

Get further review, tips and improvements! Post your working code on the sister-site: http://codereview.stackexchange.com

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM