如何看源码(方法论)
不要忽略源码中的注释
使用翻译工具
先梳理脉络,然后梳理细节即总分总,先总体过一遍,再看细节,再做一个总结
大胆猜测(8分靠猜),小心验证,再调整思路
坚持、坚持、坚持
遇到不会的,先迈过去,先不处理
坦白说看源码就是为了工作或为了技能的提升,被迫做这样的事情;
一定要掌握技巧,刚开始会很痛苦,在收获成就感之后才会慢慢产生兴趣,有了兴趣,再坚持就容易很多。
Spring IOC和AOP
原来想使用某一个对象,必须自己去new,现在spring帮我们管理和创建这些对象,这些过程不需要我们去做了,如果要用直接从容器中拿可以了。
spring创建对象大致思路(粗粒度)
看源码之前先理思路,先从spring xml方式创建对象开始,
id class是指我们要创建的那些bean对象,
第一步先读取配置文件,把它加载到当前应用程序里面去,
第二步通过解析知道有什么样的属性值、属性名称,
把这些信息都解析完成之后,再创建实例即一个具体的对象,创建出来之后,就可以直接使用了,使用完之后,还有一个销毁的过程。
再从粗粒度中拆分出一些具体细节的点
Spring里面有IOC (控制反转) 和AOP,容器是用来存放bean对象的,那用什么样的数据结构来存?
可以用list、set、map,这三个最大的区别是存取值的方式不一样,
在整个容器里面会存放n多对象,在后面使用的时候,根据一些描述来取出具体的一个值,这个时候就意味着我要根据名字来取出具体value值,所以它最终存储的是k-v格式的数据,这时候只能用map结构存储。
当把这些前置知识都搞明白了,再来细化具体的过程,spring到底是如何进行架构设计的?
当new一个对象的时候或通过其他方式创建对象的时候,才能存储到IOC容器中,存完之后,后面才能取。
这里面会涉及到创建对象的方式,创建对象有几种方式:
第一个是new
第二个是反射
在这些基础之上可以利用一些设计模式,比如用工厂模式创建对象,但不管什么样的方式,最终是要创建对象的。
创建对象的时候要思考另外一个非常核心的点,你创建出来的对象是一个通用的?还是每次都是新的?
spring bean默认的scope作用域是单例的,当然可以指定protype,但是一般情况下没有做特殊指定的话默认就是单例的,也可以指定request或session。
因为每次都new一个新的就不是一个单例对象了,所以spring就放弃了new的方式创建对象。
那反射怎么写代码
1、第一步先获取一个Class clazz=class.forName();
参数是类名.class或对象名.getClass
2、生成具体的对象
Constructor ctor= clazz.getDeclareConstructor()获取构造器,当有了构造器之后,Object obj=ctor.newInstance就可以返回一个具体对象了。
bean的描述信息或定义信息(名字、哪个class、有哪些property) 都会进行相关的存储,当这些东西都定义好之后,容器就可以启动了:
第一步先进行相关的加载,加载完之后进行一个相关的解析工作。
把它对应的一些描述信息(bean的定义信息)转换成一个具体的对象,因此定义一个接口叫BeanDefinition来表示bean的定义信息。
原来的时候是在注解或xml文件里面配置,现在加载到当前容器里面去了。
springboot用的yaml或property配置文件,属于另外一个类型的配置文件了,哪怕是json里面也可以定义bean的描述信息,只要对json进行解析就可以了。
不同的文件需要进行不同的解析工作,中间要进行一个抽象层,抽象存在的意义是定义一些相关的规范或者定义一些相关的方法,由对应的方法来完成具体的解析工作 ,那么再由具体的实现子类去进行相关的一个实现就可以了。
解析xml文件,定义一个xml的读取类;解析json文件,定义一个json的读取类;新的配置文件,依然可以定义新的实现类进行相关的读取和解析工作,这个读取的抽象类就是BeanDefinitionReader,专门用来完成读取的工作。
当还需要一些其他的配置文件的时候,只需要创建具体的子类实现这个接口进行解析就可以了,解析完之后,有一个统一的出口都放到BeanDefinition中去,就相当于提前做了一个抽象,这层抽象来完成具体对应的一个功能,此时已经有了bean的定义信息了,下一步就要把当前这个bean进行一些实例化工作,
通过这种方式可以获取到一个具体的bean对象,当我在获取bean对象的时候,是直接从当前容器里面拿的,拿的前提是一定要有。
想获取都一个完整的对象,一般包含2部分操作,第一部分叫实例化,还有一个叫初始化,实例化在堆中开辟一块空间,属性都是默认值;初始化是给属性赋值,赋值完之后已经变成一个完整对象了,就可以直接拿过来使用了。给属性赋值的操作也分为2个操作,第一个叫填充属性,另外一个叫执行初始化方法。
python中的__new__表示创建了对象,但是属性没有赋任何值,__init 完成了赋值操作;
java中没有明确的这样的一个划分,所以它包含两部分,第一个是填充属性,就表示说调用set方法给属性填充值,当把具体的一个属性填充完成之后,可以调用一个方法叫init-method,执行这个属性指定的方法里面具体实现的一个逻辑。
bean对象里面这样使用init-method和destroy-method。
实例化后下面的某个环节应该包含初始化操作,当对应的初始化完成了之后,获取到一个最终的完整对象,有了完整对象之后就可以进行一个相关的使用了。
spirng是一个框架,除了框架之外还有一点叫生态,springboot或springcloud都是在spirng基础之上演变过来的 或扩展出来的,所以在设计spring源码的时候要注意一件事情,要考虑一个非常主要的点叫扩展性,只有扩展性设计好了,后面才能随意发挥。spring框架在在整个生态里面代表的是基石或叫底层支撑。