天天看点

Spring beans架构设计原理--set注入Java BeansSpring Set注入

spring管理对象是以bean为颗粒度,在最初设计时其实是特指java beans,因此之前的注入也几乎是清一色的set注入,直到聪明的大脑们引入了annotation后两者才有了明显差异,慢慢进化出spring特有的bean规范。

本篇先从设计者的初衷java beans开始,理清楚set的注入原理,然后再(如)往(果)下(有)探(时)寻(间)annotation注入。

java beans规范主要有三点:

有一个公有的无参构造器

属性可以通过get、set、is(可以替代get,用在布尔型属性上)方法或遵循特定命名规范的其他方法访问

可序列化

sun之所以指定beans规范,很大程度上是为ide准备的——ide可以用可视化的方式设置bean的属性。

Spring beans架构设计原理--set注入Java BeansSpring Set注入

java bean规范通过java.beans.propertyeditor设置bean属性,通过beaninfo描述了javabean哪些属性是可定制以及可定制属性与propertyeditor的对应关系(propertyname->editor)。

  

beaninfo与javabean之间通过两者之间规范的命名确立,对应javabean的beaninfo采用如下的命名规范:beaninfo,如car对应的beaninfo为carbeaninfo。

jdk提供内省(introspector)暴露beaninfo,访问者通过内省间接依赖propertydescriptor从而获得bean属性描述,该描述包括该属性是否开放以及使用的属性编辑器等,最后再通过属性编辑器编辑修改属性。此外jdk提供一个默认属性编辑器的管理器--propertyeditormanager,它定义常见类型的属性编辑器(class->editor),如果某个javabean的常见类型属性没有通过beaninfo指定属性编辑器,ide将自动使用propertyeditormanager中该类型的默认属性编辑器。

对java beans的使用透着spring整个团队的灵性,rod johnson和其团队在对jdk组件以及第三方开源中间件的使用上,不仅知其然更知其所以然,并在此基础上进行了合理的精简和扩展。

举一个例子,spring中使用了aspectj,aspectj能提供了强大的静态织入能力,很多人也想当然的认为aspectj是aop的一种织入方式,事实上spring做了取舍,只集成了后者的语法,保留了自身的动态织入,利用后者解析aspectj风格的表达式并生成advisor,最终对target的织入只有jdk dynamic和cglib两种方式。

spring beans也几乎是对java beans的颠覆和升级,在后者的基础上,增加了更加强大的嵌套属性支持并有自己独特的表达式。

bean表达式和spring表达式(spring expression)是两码事,前者只是bean模块里内置简易功能,后者则是一个单独模块,两者既不是正交关系也不是平行关系。

bean表达式主要用于对象和集合的注入,语法很简单,主要有两种:

以bean.propertyname代表bean的内部属性,并支持多级嵌套。每个点代表是一级路径,路径和路径之间是嵌套和包含关系。

以propertyname[index/key]代表集合类属性的内部元素,同样也支持多级嵌套。

支持两者混合使用,举例路径a.c.b1。这代表a的属性c下的属性b是个数组或者集合(假设其为aa),则这个表达式是用来对aa的第2个元素map的name key绑定值。

Spring beans架构设计原理--set注入Java BeansSpring Set注入

spring使用统一的属性访问外观--propertyaccessor,主要包括属性可读和可写判断以及属性值的set和get。

propertyaccessor类似beanfactory,也是多层次结构,同样也是使用parent属性指向父层级。对于复合属性,会递归构建相应path的propertyaccessor,依然以a.c.b1为例,其属性访问实体结构会是:

Spring beans架构设计原理--set注入Java BeansSpring Set注入

它依赖两个实体,分别是propertyvalue和propertytokenholder:

propertyvalue是属性名和值的键值对,属性名可以是一个简单属性,也可以是复合属性即表达式。

propertytokenholder是对属性path解析的结果,解析后有actualname、canonicalname和keys三个属性,分别指实际名称、规范名称和属性下标。在上面例子中,三者分别是:

Spring beans架构设计原理--set注入Java BeansSpring Set注入

当前层级的propertyaccessor通过actualname(实际就是对象属性名)就可以找到相应的属性描述,并据此展开一系列处理,例如绑定值到集合的某个元素等等。

introspector, beaninfo, propertyeditor以及propertydescriptor四架马车在spring beans体系中依然存在, 但是后两者扮演的角色却大幅下降:

descriptor被封装在propertyhandler外观中,后者基于前者感知property类型、名称以及setter和getter等信息。

editor被放入专门的registry管理去除对descriptor和默认属性编辑器管理器的依赖。registry同时维护了默认和客户化两类editor缓存,客户化缓存有两类--类型映射(type->editor)和属性名映射(name->editor),而默认缓存则只有类型映射。

typeconvertdelegator,属性值类型转换器,依赖typedescriptor和propertyeditor,前者描述属性类型信息,包括属性本身以及泛化参数(数组、集合以及map等的元素)类型信息;后者则set以及get属性值。

cachedintrospectionresults是个多级缓存,结构如(类型->(属性名->属性描述实体))。在构造时传入类型,它通过introspector获取beaninfo,再使用后者获取所有propertydescriptor,然后将属性名和描述的关系缓存到该类型层级的缓存之下。

propertyhandler持有属性getter和bean对象,通过反射即可获得属性值。属性访问实体通过handler获取属性当前值,并将propertyvalue注入。

值绑定过程

首先从propertyvalue获取属性路径;

接着通过路径构造propertyaccessor,基于不同路径可能会存在多层级情况;

使用actualname获取属性相应的propertyhandler(propertydescriptor),并取得属性对象;

如果属性对象是集合类型,则循环keys,直到倒数第二层级。即如果是b1,返回的属性对象是b[1]这个map对象,这个map对象作为被绑定对象返回。

用绑定值绑定该属性对象,

如果绑定值和对象或集合元素类型不相符,则通过typeconverterdelegator进行转换。

Spring beans架构设计原理--set注入Java BeansSpring Set注入