它代表变量的范围的意思,它一般包括四种范围,包括request,context,session,application等,每个范围都有特定的时间和请求有效限制,
1、对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
2、对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。 容器通过获取BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。
实例化对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。
3、实例化后的对象被封装在BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。
4、紧接着,Spring根据BeanDefinition中的信息进行依赖注入。 并且通过BeanWrapper提供的设置属性的接口完成依赖注入。
5、当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现。
在spring容器初始化的时候,BeanDefinition中配置的bean的属性值一般都为String类型,如何将String类型转换为Bean中属性对应的类型呢,在这个过程中就需要用到类型转换器了。spring实例化bean过程中对属性值的转换主要是使用BeanWrapperImpl实现的。
首先来看下BeanWrapperImpl的使用
定义一个处理日期的转换器
创建一个BeanWrapperImpl用于包装目标bean(这里来模拟spring的内部实现)。然后注册Date类型的转换器,将值使用DatePropertyEditor转换为Date类型。调用setPropertyValue的时候,给testBean的birthday字段设置一个字符串类型的时间,在实际赋值的过程中会调用到类型转换器将字符串转换为日期类型。
同样可以实现一个String trim的转换器
BeanWrapper支持嵌套属性的赋值,当存在嵌套属性的时候需要设置 setAutoGrowNestedPaths=true。
接下来我们就来研究一下BeanWrapperImpl的实现过程
java.beans包中声明了一个PropertyEditor接口,用于提供对属性的访问和赋值。
PropertyEditor有个默认实现类PropertyEditorSupport,如果要自定义属性转换器,直接继承该类,然后覆写setAsText、getAsText、setValue、getValue方法。在类型转换的时候如果值是字符串会调用setAsText来赋值value,其他情况下会调用setValue来赋值value,然后再调用getValue获取改变后的value值完成类型转换。相当于将PropertyEditor当做一个加工作坊,传进去一个值,返回想要类型的值
注意 :PropertyEditor是线程不安全的,有状态的,因此每次使用时都需要创建一个,不可重用;
首先先看下BeanWrapperImpl的继承图。还有一个跟BeanWrapperImpl平级的实现类DirectFieldAccessor,用于处理getter和setter的字段访问
从图可以看出,BeanWrapperImpl实现了3个顶级接口,提供了对象属性访问、类型转换器的注册和查找、类型转换功能
从继承图可以看到PropertyEditorRegistrySupport实现了PropertyEditorRegistry接口,该类默认实现了类型转换器的注册和查找功能
在PropertyEditorRegistrySupport声明了多个存储结构用于存储不同的类型转换器来源。这里需要注意的是PropertyEditor是存在在Map中的,目标类型作为key,所以对于一个类型只能注册一个PropertyEditor,后面注册的会覆盖前面注册的
同时还看到一个conversionService变量,spring提供了另一种类型转换接口Converter,通过conversionService调用对应的Converter进行类型转换,在PropertyEditorRegistrySupport同样可以注册进来conversionService,用于使用Converter进行类型转换。conversionService的详细使用会在下篇文章中讲到。
接下来来看PropertyEditor的注册过程。
在上面提到过PropertyEditor存在在一个Map中,key是目标类型,那么这个参数propertyPath是干嘛的呢?这是为了给属性名指定专属的类型的转换器。因为一个目标类型只能有一个PropertyEditor的限制。但是有时候确实某个属性的类型转换比较特殊,这个时候就可以给这个属性名单独注册一个类型转换器,不会覆盖其他的哦。在类型转换的时候,会先根据属性名去customEditorsForPath中找可以用的PropertyEditor。
来看PropertyEditor的查找流程
首先根据属性名从customEditorsForPath查找特定的类型转换器
根据类型查找定义的类型转换器,如果没有对应的,则查找父类对应的类型转换器
PropertyEditorRegistrySupport 中默认添加了一些转换器,当调用 getDefaultEditor(requiredType)的时候会进行注册
TypeConverter接口定义了将一个值转换为目标类型的值的功能。在继承图中可以看出TypeConverterSupport对类型转换提供了默认实现。
TypeConverterSupport将类型转换功能委托给typeConverterDelegate实现
TypeConverterDelegate实现了类型转换的功能,创建TypeConverterDelegate的时候需要一个propertyEditorRegistry对象,用于查找匹配的类型转换器
首先通过propertyEditorRegistry查找自定义的类型转换器PropertyEditor和ConversionService
当该类型没有注册自定义的PropertyEditor,并且存在conversionService的时候,使用conversionService进行类型转换。如果conversionService中没有配置对应的converter,那么继续尝试使用默认注册的PropertyEditor。
如果目标类型存在自定义PropertyEditor 或者 目标类型和值类型不一样则需要进行类型转换(当没有找到ProperEditor的时候会尝试查找默认注册的PropertyEditor)
类型转换,主要分三种情况处理,值类型不是字符串,值类型是字符串数组,值类型是字符串
最后,需要对转换后的值和目标类型进行判断,是否符合要求,如果不符合继续处理。这里主要处理了集合类型还有Number类型、基本类型转String、枚举类型。
在attemptToConvertStringToEnum方法中自动根据枚举的名称转换为枚举的对象
PropertyAccessor接口定义了属性访问的功能。通过实现setPropertyValue 和 getPropertyValue方法实现对象属性的赋值和访问。
AbstractPropertyAccessor实现了PropertyAccessor接口,不过没有对setPropertyValue和getPropertyValue进行实现,而是单独提供了一个 setPropertyValues的方法, 用于批量设置属性值,同时可以通过 ignoreUnknown和ignoreInvalid参数忽略未知的属性
在AbstractNestablePropertyAccessor类中实现了setPropertyValue和getPropertyValue功能。
由于存在嵌套属性赋值的情况,对于嵌套的处理,其实只需要对嵌套的最底层进行类型转换,上层每一层就创建默认的值然后set到再上层对象的属性中。在这里spring使用了递归解决这个问题,创建每一层属性的对象值,使用BeanWrapper包装该对象,那么又是一个BeanWrapperImpl的赋值流程。
来看这个递归的内部实现,在getNestedPropertyAccessor完成对外层属性的初始化和将该值赋值到所依赖的对象中。然后使用BeanWrapper封装属性对象,后续走属性对象的赋值流程
创建属性对象,并将该对象set到宿主对象。因为对象是指针引用的,所以在这步已经完成对宿主对象的属性赋值,接下来的流程只要对属性对象中的依赖属性进行赋值。
使用递归解决了嵌套赋值的问题,那么接下来就是针对最底层BeanWrapperImpl的属性赋值流程
在processLocalProperty方法中,首先通过子类获取属性处理器,通过PropertyHandler对属性赋值。在赋值之前再次判断属性是否已经进行了类型转换,如果没有再次调用类型转换器进行转换,如果已经完成类型转换,使用ConvertedValue
对属性的访问和设置spring进行了更小粒度的封装。提供了 PropertyHandler抽象类。为什么在这里进行抽象,看PropertyHandler的两个实现,可以看到一个是BeanPropertyHandler,一个是FieldPropertyHandler,不难想象,属性一种是由getter和setter方法进行访问,一种是没有getter和setter直接反射字段进行的。
如果要对map进行控制,我们可以再提供一个专门处理map的实现了类handler
BeanWrapperImpl提供了BeanPropertyHandler,将setter和getter传入
BeanPropertyHandler提供对setter和getter的访问
AbstractNestablePropertyAccessor的另一个实现类DirectFieldAccessor,专门用于给字段赋值,不依赖setter和getter,那么这个是怎么实现的,看源码发现是DirectFieldAccessor中提供了一个PropertyHandler的实现类,通过Field的反射实现了setValue和getValue
Spring 从核心而言,是一个DI 容器,其设计哲学是提供一种无侵入式的高扩展性框架。即无需代码中涉及Spring专有类,即可将其纳入Spring容器进行管理。
作为对比,EJB则是一种高度侵入性的框架规范,它制定了众多的接口和编码规范,要求实现者必须 遵从。侵入性的后果就是,一旦系统基于侵入性框架设计开发,那么之后任何脱离这个框架的企图都将付 出极大的代价。
为了避免这种情况,实现无侵入性的目标。Spring 大量引入了Java 的Reflection机制,通过动态 调用的方式避免硬编码方式的约束,并在此基础上建立了其核心组件BeanFactory,以此作为其依赖注入机制的实现基础。
org.springframework.beans包中包括了这些核心组件的实现类,核心中的核心为BeanWrapper 和BeanFactory类。这两个类从技术角度而言并不复杂,但对于Spring 框架而言,却是关键所在。
InputStream is = new FileInputStream("bean.xml")XmlBeanFactory factory = new XmlBeanFactory(is)Action action = (Action) factory.getBean("TheAction")
此时获得的Action实例,由BeanFactory进行加载,并根据配置文件进行了初始化和属性设定。
联合上面关于BeanWrapper的内容,可以看到,BeanWrapper实现了针对单个Bean的属性设 定 *** 作。而BeanFactory则是针对多个Bean的管理容器,根据给定的配置文件,BeanFactory从中读取 类名、属性名/值,然后通过Reflection机制进行Bean加载和属性设定。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)