如下图描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程及参与的角色:
(1)ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件资源。
(2)BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中的每个
(3)容器扫描BeanDefinitionRegistry中的BeanDefunition,使用Java反射机制自动识别出Bean工厂后处理器(实现BeanFactoryPostProcessor接口的Bean),然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成两项工作:
(4)Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy着手进行Bean实例化的工作。
(5)在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装。BeanWrapper以Java反射机制操作Bean的方法,它将结合该Bean的BeanDefinition及容器中的属性编辑器,完成Bean属性注入工作。
(6)利用容器中注册的Bean后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续加工直接装配出一个准备就绪的Bean。
Spring组件按其所承担的角色可分为两类:
(1)物料组件:Resource、BeanDefinition、PropertyEditor及最终的Bean等,它们是加工流程中被加工、被消费的组件。
(2)设备组件:ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy及BeanWrapper等,它们负责对物料组件进行加工处理。
以下将对这些组件进行介绍:
如下图是BeanDefinition的类继承结构
在配置文件中的父
创建BeanDefinition主要包括两个步骤:
(1)利用BeanDefinitionReader读取承载配置信息的Resource,通过XML解析器解析配置信息的DOM对象,简单地为每个
(2)利用容器中注册的BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将以占位符表示的配置解析为最终的实际值,这样半成品的BeanDefinition就成为成品的BeanDefinition。
InstantiationStrategy负责根据BeanDefinition对象创建一个Bean实例,其类继承结构如下所示:
SimpleInstantiationStrategy利用Bean实现类的默认构造函数、带参构造函数或工厂方法创建Bean的实例。
CglibSubclassingInstantiationStrategy扩展了SimpleInstantiationStrategy,为需要进行方法注入的Bean提供了支持。它利用CGLib类库为Bean动态生成子类,在子类中生成方法注入的逻辑,然后使用这个动态生成的子类创建Bean的实例。
InstantiationStrategy仅负责实例化Bean的操作,相当于执行Java语言中new的功能,它不会参与Bean属性的设置工作。
Spring委托BeanWrapper完成Bean属性的填充工作。在Bean实例被InstantiationStrategy创建出来之后,容器主控程序将Bean实例通过BeanWrapper包装起来,这是通过调用BeanWrapper#setWrappedInstance(Object obj)方法完成的。BeanWrapper的类继承结构如下:
PropertyAccessor接口定义了各种访问Bean属性的方法,PropertyEditorRegistry是属性编辑器的注册表。所以BeanWrapper实现类BeanWrapperImpl具有三重身份:
(1)Bean包裹器(2)属性访问器(3)属性编辑器注册表
一个BeanWrapperImpl实例内部封装了两类组件:被封装的待处理的Bean,以及一套用于设置Bean属性的属性编辑器。
Spting主控程序从BeanDefinition中获取Bean属性的配置信息PropertyValue,并使用属性编辑器对PropertyValue进行转换以得到Bean的属性值。对Bean的其他属性重复这样的步骤,就可以完成Bean所有属性的注入工作。BeanWrapperImpl在内部使用Spring的BeanUtils工具类对Bean进行反射操作,设置属性。
将配置信息独立到一个外部属性文件中,并在Spring配置文件中通过形如${user}、${password}的占位符引用属性文件中的属性项。这种配置方式拥有两个好处:
(1)减少维护的工作量:配置信息发生改变时,只需要调整独立的属性文件即可。
(2)使部署更简单
Spring提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessorBean接口,也是一个Bean工厂后处理器。
使用一个名为jdbc.properties的配置文件,如下代码:
dbName=sampledb
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/${dbName}
userName=root
password=123456
下面通过PropertyPlaceholderConfigurer引入jdbc.properties属性文件
PropertyPlaceholderConfigurer的属性
locations:如果只有一个属性文件,直接使用location属性指定,如果有多个属性文件,则使用locations属性进行设置,可以像配置List一样配置locations属性。
fileEncoding:属性文件的编码格式。
order:如果配置文件中定义了多个PropertyPlaceholderConfigurer,则通过该属性指定优先顺序。
placeholderPrefix:通过${属性名}引用属性文件中的属性项,其中"${"为默认的占位符前缀,可以根据需要改为其他的前缀符。
placeholderSuffix:占位符后缀,默认为"}"。
使用
基于注解及基于Java类的配置中引用属性:
通过@Value注解为Bean的成员变量或方法入参自动注入容器已有的属性,如下:
@Component
public class MyDataSource {
@Value("${driverClassName}")
private String driverClassName;
@Value("${url}")
private String url;
@Value("${userName}")
private String userName;
@Value("${password}")
private String password;
public String toString(){
return ToStringBuilder.reflectionToString(this);
}
}
PropertyPlaceholderConfigurer中的下列方法用于在属性使用前对属性进行转换:
(1)void convertProperties(Properties props):属性文件中的所有属性值都封装在props中,覆盖此方法,可以对所有的属性值进行转换处理。
(2)String convertProperty(String propertyName,String propertyValue):在加载属性文件并读取文件中的每个属性时,都会调用此方法进行转换处理。
(3)String convertPropertyValue(String originalValue):同上一个方法类似。
Spring允许在属性文件中使用${propName}实现属性之间的相互引用。
在Spring3.0中,可以通过类似#{beanName.beanProp}的方式引用另一个Bean的值。在基于注解和基于Java类配置的Bean中,可以通过@Value("#{beanName.propName}")的注解形式引用Bean的属性值。
ApplicationEvent的唯一构造函数是ApplicationEvent(Object source),通过source指定事件源,它有两个子类。
(1)ApplicationContextEvent:容器事件,它拥有4个子类,分别表示容器启动、刷新、停止及关闭的事件。
(2)RequestHandleEvent:这是一个与Web应用相关的事件,当一个Http请求被处理后,产生该事件。只有在web.xml中定义了DispatcherServlet时才会产生该事件。她拥有两个子类,分别代表Servlet及Portlet的请求事件。
事件监听器接口
如下图是事件监听器接口的类图
ApplicationListener接口只定义了一个方法:onApplicationEvent(E event),该方法接收ApplicationEvent事件对象,在该方法中编写事件的响应处理逻辑。
SmartApplicationListener接口是Spring3.0新增的,定义了下面两个方法:
(1)boolean supportsEventType(Class extends ApplicationEvent> eventType):指定监听器支持哪种类型的容器事件,即它只会对该类型的事件作出响应。
(2)boolean supportsSourceType(Class> sourceType):指定监听器仅对何种事件源对象作出响应。
GenericApplicationListener是Spring4.2新增的,定义了两个方法:
(1)boolean supportsEventType(ResolvableType eventType):指定监听器支持哪种类型的容器事件,即它只会对该类型的事件作出响应。ResolvableType 是Spring4,.0提供的一个更简单的泛型操作支持类。
(2)boolean supportsSourceType(Class> sourceType):指定监听器仅对何种事件源对象作出响应。
事件广播器Spring为事件广播器定义了接口,并提供了实现类,如下: