3

I have a SuperClass.

@Component
public class SuperClass {
    private TestBeanSuper testBeanSuper;

    public SuperClass(){}

    @Autowired
    public SuperClass(TestBeanSuper testBeanSuper) {
        this.testBeanSuper = testBeanSuper;
    }

    public void test() {
        testBeanSuper.test();
    }
}

a SubClass.

@Component
public class SubClass extends SuperClass{
    private TestBeanSub testBeanSub;

    public SubClass(){}

    @Autowired
    public SubClass(TestBeanSub testBeanSub) {
        this.testBeanSub = testBeanSub;
    }

    public void test() {
        super.test();
    }
}

When I call subClass.test(), NullPointerException occurred. The testBeanSuper in SuperClass test method is null.

java.lang.NullPointerException: null
    at com.spring.demo.SuperClass.test(SuperClass.java:18) ~[classes/:na]
    at com.spring.demo.SubClass.test(SubClass.java:18) ~[classes/:na]
    at com.spring.demo.TestController.test(TestController.java:19) ~[classes/:na]

enter image description here

Could someone tell me why this happened?

And when I debugged, I found that testBeanSuper is inside the SubClass instance. The testBeanSuper is defined as private in SuperClass, why it is inside the SubClass instance? enter image description here

huashui
  • 1,758
  • 2
  • 16
  • 24

2 Answers2

3

Because you might not have any Inheritance in your Bean Definition. Super-class members, loaded into memory during creation of respective child-class according to the Java OOP model, is not going to be referencing your explicit Spring Bean. I. E. you have Java inheritance, but you should also define Bean Inheritance, if you'd want to achieve what you ask in your question. Otherwise, your explicit component is not necessarily the one where your "child" component inherits from.

This is also the answer for your

And when I debugged, I found that testBeanSuper is inside the SubClass instance. The testBeanSuper is defined as private in SuperClass, why it is inside the SubClass instance?

second question.

One more general suggestion:
whenever you deal with multiple beans connected/wired to each other in any form, in the most cases, better to use setter-injection to avoid any cycling problems or .. bean is in creation state problems.

I would also like to suggest you to read about Bean Definition Inheritance.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
  • Thank you for your answer. I understood I need to specify Bean Inheritance, but could you tell me how to add bean inheritance when annotation is used. I searched and the solutions are all about how to add bean inheritance in XML. – huashui Dec 14 '19 at 12:45
  • Not to delay the answer: I'm away for a while for now, so can't do the good research, and I can't recall it from the top of my head at the moment.. but what if you define your custom `@Bean` in the `@Configuration`, instantiate and initialize your object, and then register it into your context? – Giorgi Tsiklauri Dec 14 '19 at 13:17
  • Thank you. I found the solution which just remove @Component from SuperClass and change constructor injection to field injection. Then everything is OK. – huashui Dec 16 '19 at 04:45
  • I thought I also mentioned setter-injection.. hah :), at least, I was thinking about that as well. During the instantiation, try to avoid all cycling dependencies.. and therefore, use setter as much as possible. – Giorgi Tsiklauri Dec 16 '19 at 07:07
  • Also, I'd very much appreciate if someone, who voted this answer down, would explain to me why they did so. – Giorgi Tsiklauri Dec 16 '19 at 07:08
1

There seems to be no annotation-based alternative for the XML parent bean mechanism and the corresponding issue has been closed. This answer provides a workaround though: if you autowire the member instead of the constructor, the TestBeanSuper will be properly injected.

@Component
public class SuperClass {
    @Autowired
    private TestBeanSuper testBeanSuper;

    public SuperClass(TestBeanSuper testBeanSuper) {
        this.testBeanSuper = testBeanSuper;
    }

    public SuperClass(){}

    public void test() {
        testBeanSuper.test();
    }
}

On the matter of private members showing up in the debugger - there are already excellent explanations. While the subclass does not inherit private members nor has access to them, there is only one object at runtime which needs to have access.

ldz
  • 2,217
  • 16
  • 21