前前后后写了几篇Spring的源码分析,看着寥寥无几的阅读量,我意识到了自己写的不够好,虽然我的流程图、源码注释花了不少时间,可阅读量摆在那,就是不够好。
思来想去,我决定再加一篇更加通俗、直观的文章:假如让我们来设计Spring框架,我们该怎么设计,我们会想到什么问题,又该如何解决这些问题。
好的开始吧。
让我们回到2008年7月11日的早晨。
最近我们在项目开发中,遇到了如下的问题,感觉很繁琐,我想搞一个框架出来,方便日后的项目开发:
- 项目中有很多服务类对象,对象间引用极其麻烦,都用单例模式的话,那方法都是
public
,这样设计很不好,不符合Java的封装特性。应该是哪个类被需要,就聚合哪个类,而不是都暴露出来。使用聚合的话,那构造对象就很麻烦,一开始需要聚合3个服务,过几天又加了两个服务依赖,那对象构建的逻辑又要改了,一两个对象无所谓,大型项目里,依赖关系错综复杂,维护起来甚是麻烦。 - 很多服务类对象其实都是单例的,不需要频繁创建,大量重复对象创建导致频繁GC,服务器受不了。
- 处理对象聚合的问题,给相互聚合的对象进行初始化时候,还得特殊处理,不然会进入死胡同。
- 有很多优秀的第三方框架,我想让他们能够无痕无感地接入到我的框架里,就好像是我自己的组件一样,让开发者很方便的调用,而不用去做很多配置,这又该如何做。
问题还有很多,但是以上问题便是我设计框架的立根之本,不把这些问题解决了,那这个框架也只是一个终将被时间遗忘的代码,把这些功能实现了,再去开枝散叶才是长久之计。
Bean
我的框架要有一些自己的特点,我把对象都统称为Bean。(豆子?咖啡豆?Java本来就是和咖啡有半毛钱关系)
先搞个容器来存放Bean
项目中有这么多单例Bean,而且Bean构建过程可能也颇为复杂,那我就把他们存起来。用什么存呢?Map呗
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
单例、非抽象、非懒加载
什么Bean都可以放进我的缓存里边么?没必要吧,我只需要对单例的&&非抽象的&&非懒加载的Bean给放进缓存里就行了。
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
......
}
拆解Bean的创建过程
前面提到相互聚合即循环依赖的问题,那我就将Bean的创建分成3个步骤:实例化、属性赋值、初始化。
这样子,循环依赖的Bean,就可以先实例化,然后把半成品Bean(未属性赋值和初始化的对象)赋值给当前Bean,这样流程就可以往下走,最后出来的肯定是完好的Bean。
if (instanceWrapper == null) {
// 实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
try {
// 填充属性
populateBean(beanName, mbd<