阅读引导:
1、成人学习,由点破面。
2、乔布斯说 connecting the dots,对于程序开发来说,也是如此,通过遇到的问题把知识点串联起来。
3、为自己工作,为自己的系统工作,做自己的老板,形成正循环:打磨当前工作的核心关键能力——>高效能工作——>更多时间打磨自己的系统——>更高效能工作——>打磨下个层次工作的核心关键能力……
4、核心竞争力,是指你拥有的(独特的)知识经验组合,经过你思维逻辑的组织梳理,在实践中产生无可替代的价值。打造自己的TMS系统(T:专业技术;M:沟通管理、S:行业解决方案),利用复利效应,让系统为自己工作。
团队中一个同事遇到了一个问题,想要把bService注入到Aservice中去使用,为什么AService中的bService属性总是为空?
下面的代码,为了简单起见,都简化成无实际意义的例子
有一个发送消息的策略类BService:
@Service
public class BService implements SendMsgService{
@Override
public void sendMsg(String msg) {
// TODO Auto-generated method stub
}
}
另外,假设还有一个组装发送消息服务的发送类:
public class MsgPublisher {
private SendMsgService service;
public MsgPublisher(SendMsgService service) {
this.service = service;
}
public void send(String msg) {
service.sendMsg(msg);
}
}
最终调用服务:
@Service
public class AService {
@Autowired
private SendMsgService bService;
private MsgPublisher msgPublisher = new MsgPublisher(bService);
public void send(String msg) {
msgPublisher.send(msg);
}
}
这段代码有什么问题,为什么在调用AService类的send方法的时候,总是抛出空指针异常?
跟踪进去后发现,bService这个属性一直为空。
在找到我之前,开发同事找人帮他看了,也自己排除了语法。
首先,注解都没有问题,没有配置错。
其次,为了证明bean都已经加载,添加了许多打印语句,证明bService确实是加载到spring的容器中了。
也就是说,初步排除了语法或者API使用问题。
为了启发他解决问题,直接让他使用了spring 的InitializingBean,在afterPropertiesSet方法中,对MsgPublisher进行初始化,变成这样,问题就解决了。
@Service
public class AService implements InitializingBean{
@Autowired
private SendMsgService bService;
private MsgPublisher msgPublisher;
public void send(String msg) {
msgPublisher.send(msg);
}
@Override
public void afterPropertiesSet() throws Exception {
msgPublisher = new MsgPublisher(bService);
}
}
那么,为什么换一种方式就可以了呢?
实际上,spring容器对于bean的加载管理,是在Java类本身的加载之后的。
也就是说,首先进行Java类的加载,先得要初始化,就会先把static的变量、代码块加载,然后就是类变量等,那么首先:
private MsgPublisher msgPublisher = new MsgPublisher(bService);
加载的时候,bService还是空。
而在Java类加载成功之后,spring容器采取扫描,去把类解析beandefination,解析成bean管理。
而在spring容器中,只有getBean的时候,才会去对依赖的注解进行注入。
所以此种方法永远获取不到bService的实例。
通过这个问题,实际上还可以继续深入,了解bean的初始化流程,生命周期到底是什么样子的。
就能把spring bean的生命周期和Java类的生命周期结合起来。
再继续向下深入,spring的容器核心,是怎么个初始化流程?对外暴露了哪一些某个生命周期阶段进行管理的接口?
例如InitializingBean,那么销毁的时候呢?
整个容器本身又对外提供了哪些钩子,可以让开发者进行扩展?
这种设计思想,对于自己设计框架有何帮助?
在阅读源码过程中,是不是可以看到PathResource类,还需要自己去采用Java原生IO类去读取文件么?
PathMatchingResourcePatternResolver类能做什么?对于properties的加载,还需要自己写吗?
问题——》Java类加载——》spring bean声明周期——》常用API——》spring IoC容器源码——》工具类——》设计思想。