这是一个比较自闭的SpringIOC问题,有一天前同事突然找到我问这个问题。下图链接:https://www.cnblogs.com/virgosnail/p/10257040.html
(这个链接的博主其实最终还是没搞懂,或者说没说清楚)
问题大概是这样的,我有一个A类,里面有一个方法,然后我在B类中调用这个方法,然后我在C类(Controller)中new一个B类的对象,调用B类中的调用A类的方法(说到这里熟悉springIOC的同学应该知道问题了,原谅我比较菜)
一开始我被他带着节奏走,(也怪自己spring不扎实,后面需要改进)认为可能是spring的bean的生命周期的问题。debug了一晚上按下不表,我们来看看我重现的代码:(基于springBoot环境)
最深层被调用的A类:
import org.springframework.stereotype.Component;
@Component
public class AClassForIOC {
private String a = "a";
public String getA(){
return a;
}
}
调用A类的B类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BClassForNew {
@Autowired
AClassForIOC aClassForIOC;
public void puA(){
aClassForIOC.getA();
}
}
以及我们万恶的Controller,C类:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/test")
public void test(){
BClassForNew a = new BClassForNew();
a.puA();
}
}
然后我们调用一下:
万恶的a类居然是空的?
不出意外的,B类的方法调用抛空指针了。
于是,同事给我的结论是,可能就是因为这种调用方式导致A类没有创建。
待老夫检查一下SpringIOC容器里面是否有这些个对象啊:
因为之前读过源码,大概知道在SpringApplication.run()方法里的哪个地方能看到我们的IOC容器。
找到variables里的context->beanFactory->beanDefinitionMap。
这里beanDefinitionMap是一个ConcurrentMap,而beanDefinitionNames是一个ArrayList,前者是键值对<key:类名小驼峰,value:管理的对象>,后者是类名。beanDefinitionMap就是我们IOC容器的bean管理员本体。
从beanDefinitionMap找到了这三个对象,那么说明其实跟bean的生命周期没有什么关系。不相信的话,其实可以改一下A,B类的构造方法,打印一句话,就能在启动日志中看到,已经new了并且初始化了。
那么,为什么调用B的方法时,A是空指针?
其实答案已经呼之欲出了。
人家Spring确实把a实例对象注入到了b实例对象中。可是这里用的b实例对象并不是人家Spring构建的,而是我们自己new的,也就用不上IOC,扯不上依赖注入。自己new的对象自己负责初始化。
我们将TestController改成如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
BClassForNew bClassForNew;
@RequestMapping("/test")
public void test() {
// BClassForNew a = new BClassForNew();
// a.puA();
bClassForNew.puA();
}
}
这里我们将用Spring给我们构建的B类实例。
此时控制台没有再报错。
总结,说起来羞愧,这次的debug同事花了将近一周,我花了两天,最后我还是一步步debug才到的问题。(虽然下载了Spring源码,也没看出来,debug大法好)
除了一开始被带歪了,更重要的是我自身Spring也不算扎实。所以学习Spring源码应该提高一下优先级,毕竟菜鸡要多学学大佬的写法。希望今后不会出现这种低端bug。