Spring 源码导读

java高级架构师

做为Java开源世界的第一框架,Spring已经成为事实上的Java EE开发标准Spring框架最根本的使命是简化Java开发,因此学习、研究、掌握Spring框架成为每一位Java开发人员的必修课。而阅读源码则是学习Spring的最好方式之一。

Spring里面最重要的特性就是Ioc,可能你还会说aop。其实aop的实现也是基于ioc。Ioc(Inversion of Control),即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

关于Spring IOC源码分析的文章网上很多,现在我就来另辟蹊径。Spring Ioc的对象扭转以及涉及到的核心接口来分析一下它的源码实现。

我把Spring Ioc的对象转换分为以下4个步骤:

Resource -> BeanDefinition -> BeanWrapper -> Object

1 Resource

Resouce 其实是一个接口,代表的是资源,在计算机里面从一个地方移动到另外一个地方所需要的东西就是数据流,所以 Resource 实现了 InputStreamSource 接口,通过 InputStreamSource 接口可以获取到 Inputstream,这样就可以读取不同的 Bean 定义了。

publicinterface InputStreamSource {

   InputStream getInputStream() throws IOException;

}

Spring可以定义不同类型的bean,最后都可以封装成Resource通过IO流进行读取。 Spring可以定义类型的bean对象:

XML:这是Spring最开始定义bean的形式

Annotation :由于通过XML定义bean的繁琐,Spring进行了改进可以通过@Component以及基于它的注解来定义bean。例如:@Service,@Controller等等,它们都可以定义bean ,只不过语义更加明确。

Class:通过@Configuration与@Bean注解定义,@Configuration代理xml资源文件,而@Bean代替标签。

Properties/yml:通过 @EnableConfigurationProperties 与 @ConfigurationProperties 来定义bean。这种形式在Spring boot自动注入里面大量使用。

2、BeanDefinition

望文生义,很显示这个是 Bean 对象的定义。 Spring通过不同形式来定义bean,最终会把这些定义转化成BeanDefinition 保存在Spring容器当中进行依赖注入。下面我们来看一下BeanDefinition的接口定义。

这个接口的定义很复杂但是,对于初始理解spring ioc,只需要关心两个方法。

getConstructorArgumentValues:获取构造器注入定义的参数。

getPropertyValues:获取setter注入定义的参数。

所以Spring支持构造器注入与setter依赖注入。

3、BeanWapper

其实什么是依赖注入,简单来说就是Spring帮我们创建对象。把创建对象写死在Java文件变成了通过不同的Spring配置可以注入不同的值。创建对象的职责由Java文件变成了Spring配置文件。

下面我就问一个简单的问题,如何创建对象。可能大家呵呵一笑,创建对象这还不简单。

其实 Spring 也是这样来创建对象的,不信讲看 : (入口方法 BeanFactory#getBean)

AbstractAutowireCapableBeanFactory#createBeanInstance() :通过反射Constructor调用配置的无参或者有参来创建对象实例。通过BeanDefinition#getConstructorArgumentValues 获取,并返回 BeanWrapper 对象。

AbstractAutowireCapableBeanFactory#populateBean():,获取到定义的bean生成的所有的定义的setter注入的属性(BeanDefinition#getPropertyValues),然后遍历些这些属性,通过内省获取到对象所有的 属性描述器(PropertyDescriptor),获取到,属性的PropertyDescriptor#getWriteMethod方法,也就是setter方法,依赖注入值。如果是普通属性或者一些复杂对象,比如普通属性String, int, long或者classpath:*转换为Resource复杂对象等等,直接注入即可;对于引用类型对象,继续依赖注入直到所有的属性是普通属性为止。

AbstractAutowireCapableBeanFactory#initializeBean():调用 Spring 自定义的初始化方法比如:BeanPostProcessor 扩展以及 init-method。

实例化对象返回BeanWrapper,其实是为了依赖注入服务也就是上面的第二步。 这个接口的功能还是很复杂的,它继承了 4 个接口。

TypeConverter

PropertyEditorRegistry

PropertyAccessor

ConfigurablePropertyAccessor

下面就来分别介绍一下这些接口的功能。

3.1 TypeConverter

下面就是这个接口的定义。

它的作用就是自动类型转换,因为Spring作得太无感知了。你也许还没有感觉到它的存在。没关系,我提示一下你应该就会明白。比如,声明一个用户对象这个对象既有String类型的名称,又有Int类型的年龄。Spring怎么知道属性的类型呢?这个就是 Spring的自动类型转换。关于Spring的自动类型转换 我在之前就已经分析过了。

3.2 PropertyEditorRegistry

这个接口主要的作用是注册属性修改器(PropertyEditor),这个是 Java 内省里面的机制。

一般通过继承 java.beans.PropertyEditorSupport 来实现自定义的类型转换。在 Spring 内部有大量的实现,如下图所示

3.3 PropertyAccessor

PropertyAccessor这个接口是判断对象中的某个属性是否可读/可写,并且可以定入或者读取某个属性的值。从这个接口定义我们可以看出,它的使用其实就是真正用来依赖注入的。然后调用属性操作的写入操作,完全依赖注入。

3.4 ConfigurablePropertyAccessor

这个接口的功能和PropertyEditorRegistry接口一样,只不过后者是通过java内省来进行类型自动转换,而ConfigurablePropertyAccessor接口是通过Spring自己定义的org.springframework.core.convert.ConversionService来作类型转换类型转换。在Spring中默认使用的DefaultConversionService来作自动类型转换支持,并且内部还添加了很多默认的类型转换。

关于Spring的自动类型转换 我在之前就已经分析过了。和java内省的原理是一样的。

3.5 BeanWrapper

这个接口就挺简单了,通过实现了上面的几个接口具有了依赖注入、类型转换注册(java 内省或者Spring自定义的自动类型转换)。然后这个接口的主要的作用就是通过调用getWrappedInstance方法获取到当前实例对象,提供给属性的writer方法进行依赖注入。

writeMethod.invoke(getWrappedInstance(),value);

1

4、总结

让我们再来看一下Spring的对象扭转过过程:

Resource -> BeanDefinition -> BeanWrapper -> Object

相信基于以上的讲解,大家对于上面的过程能够理解Spring IOC的项目过程。在Spring 进行 依赖注入的时候,首先把这种资源转化成Resource抽象,通过里面的IO流读取定义的bean。然后再转化成BeanDefinitioin,里面定义了包括构造器注入,以及setter注入的定义。最后通过BeanWrapper这个接口,首先获取定义的构造器注入属性,通过反射中的Constructor来创建对象。基于这个对象,通过java里面的内省机制获取到定义属性的属性描述器(PropertyDescriptor),调用属性的写入方法完成依赖注入,最后再调用Spring的自定义初始化逻辑,主要包括以下三个扩展点:

BeanPostProcess,Spring aop就是基于此扩展。

Init-method,可以在 bean 标签通过 init-method定义,也可以实现InitializingBean

XXXAware,Spring容器感知类,可以在bean里面获取到 Spring容器的内部属性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容