Spring源码分析(一)——IOC容器的思考

Java后端开发有很多框架,最著名的就是Spring了。这个框架已经存在了快20年了,经过不断的更新迭代,成为了目前最火热的后端开发框架,并且形成了一个庞大的社区,衍生出很多系统设计的思想以及实现。在享受Spring带给我们开发便利的同时,了解背后的设计原理有助于我们更好的使用它,甚至在巨人的肩膀上进行二次开发,为开源社区贡献力量。

本专题将深入到Spring框架的源码中去,网上看到好多对于Spring源码的总结,大部分都是代码的堆砌,缺少在分析源码过程中的层层递进的思考过程,所以打算出个专题去记录自己分析框架时候的思考。

关于阅读源码的技巧:

(1)先要会用,跑一个简易的Demo,看看运行效果是什么。
(2)看官方的API,或者Reference,了解框架设计的思想,API手册有时候说明了一切。
(3)通过在自己Demo上设置断点调试理解几个基本功能的实现方式。
(4)框架的创新性方法研究(同样是打断点进行调试)

一定要带着问题去看代码,项目的代码量是巨大的,不需要每一行都看,重点是把握框架的主要功能设计实现,遵循二八原则,学会抓住那百分之二十重要的东西。

通过查看官网的Reference,Spring的核心是IOC(控制反转)和AOP(面向切面编程)

问题一:IOC的目的是什么?(发明它为了解决什么问题?)

只要写过Java的人都知道新建POJO(普通Java对象)需要使用new关键字,然后才能使用对象A内定义的方法。

A a = new A();
a.doSomething();

(new关键字背后原理可以参考跟我一起学习JVM(二)——Java对象的幕后)

那平时的研发项目肯定会存在多个类,并且类与类之间相互依赖,比如以下情况:

class A{
  void doSomething(){}
  ...
}

class B{
  A a = new A();
  a.doSomething();
  ...
}

这个时候我们发现在B类中含有对A的依赖,如果A的代码修改了,那么B中的代码可能也要进行修改。这种依赖结构会造成代码之间耦合性高,代码不容易进行修改扩展。

实际情况下的开发项目中类非常多,少则几十个多则甚至上百个类,如果他们对A都有依赖,修改一处代码需要同时对上百个类进行修改,万一改错了还运行不了,那工程师们肯定都辞职了。

follow up : 如何避免在不同类中新建类所造成的耦合问题?

能否用一个中间件middleWare去帮助新建对象,类似于一个管家,每次要使用的时候由这个中间件去帮助新建,并且把实例对象返回给调用者?

最好使得B中A的初始化变成如下:

class B{
  // B告诉middleWare我需要A这个类,接着用a去接收middleWare传过来的A对象
  A a = middleWare.get("A.class");
  a.doSomething();
  ...
}

通过告诉middleWare我需要A这个类的对象实例,然后它自动就返回给我A的对象。这样不管以后系统怎么修改A类的内容,甚至包括A的构造器,在B类中总是要求获取整个A对象,所以A的修改对B的改动没有影响,这就是解除耦合。

而IOC容器就是充当着这个middleWare的角色,类似于管家管理着所有的类

随之而来的问题是…

问题二:这个middleWare如何能根据用户需要创造出对应的类呢?
仅仅获得类名字肯定是不够的,至少要知道类里有哪些参数,构造函数是啥样子的,有没有和其他类有继承关系啊…因为这些都是一个类最重要的信息,所以要创造一个东西的前提是要对这个东西足够的了解。

思考:这里我想到一个Java常用的反射技术,是运行过程中动态获取类信息常用的方法,那IOC最终会不会就是通过反射来实现的呢?

于是我们进一步想:如果要把对象给调用者,这个middleWare必须要有对象的基本信息,这些基本信息能否也提取出来,统一放到一个文件里,管家在系统开始启动的时候读一遍这个文件不就知道了所有的对象的信息了么,而且这样又可以简化系统。

思考:基于xml, file…的外部配置文件诞生原因是不是就是这个?

既然是管理对象的管家,那他肯定给每个对象设置了一个通用的格式,比如把每个对象都抽象成为一个ObjectTemplate,因为对象与对象之间很可能存在依赖关系,那管家肯定需要为每个对象都维护一张表,记录与这个对象关联的所有对象信息。

思考:也许可以用Map<ObjectTemplate, List>这种类型的数据结构或者就是数组去存储?后面一起去考证!

问题三:系统运行结束,容器使用完之后,我们需要把容器中对象销毁掉从而释放内存,如何回收容器中已经创建了的对象?

思考:是不是可以通过对象间的依赖关系表(HashMap)将对象自己存储的依赖键值对逐个调用remove(),就能达到清空关系表的目的?

follow up: 如果有一个时间段很巧,程序中很多地方同时操作同一个对象的实例(特别是这个对象为单例的时候),如何确保线程安全?还是说Spring已经帮助实现了线程安全的获得对象的过程?

思考:这时候就需要和之前学习的并发编程知识联系在一起了!考证发现这是Spring容器目前的一个缺憾,容器并不保证容器中对象的线程安全性,需要自己去实现,可以使用ThreadLocal去定义变量,或者创建对象时要避免有状态的参数。(我们居然找到了一个Spring目前还未有解决方案的小问题!)

下一章就让我们带着这些问题深入到源码中去找寻答案,揭开IOC的真面目。

喜欢的话转发关注噢,这是我创作的动力!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值