您的位置:首页 > 编程语言 > Java开发

Spring4学习:Spring容器高级主题

2017-03-17 20:52 423 查看

一、Spring容器技术内幕

如下图描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程及参与的角色:



(1)ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件资源。

(2)BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中的每个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。

(3)容器扫描BeanDefinitionRegistry中的BeanDefunition,使用Java反射机制自动识别出Bean工厂后处理器(实现BeanFactoryPostProcessor接口的Bean),然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成两项工作:

对使用占位符的<bean>元素标签进行解析,得到最终的配置值。
对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过Java反射机制找出所有属性编辑器的Bean(实现java.beans.PropertyEditor接口的Bean),并自动将它们注册到Spring容器的属性编辑器注册表中(PropertyEditorRegistry)。

(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等,它们负责对物料组件进行加工处理。

以下将对这些组件进行介绍:

1、BeanDefinition

如下图是BeanDefinition的类继承结构



在配置文件中的父<bean>用RootBeanDefinition表示,子<bean>用ChildBeanDefinition表示,而没有父<bean>的<bean>则用RootBeanDefinition表示。

创建BeanDefinition主要包括两个步骤:

(1)利用BeanDefinitionReader读取承载配置信息的Resource,通过XML解析器解析配置信息的DOM对象,简单地为每个<bean>生成对应的BeanDefinition对象。但是这里生成的BeanDefinition可能是半成品,因为在配置文件中,可能通过占位符变量引用外部属性文件的属性,这些占位符变量在这一步里还没有被解析出来。

(2)利用容器中注册的BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将以占位符表示的配置解析为最终的实际值,这样半成品的BeanDefinition就成为成品的BeanDefinition。

2、InstantiationStrategy

InstantiationStrategy负责根据BeanDefinition对象创建一个Bean实例,其类继承结构如下所示:



SimpleInstantiationStrategy利用Bean实现类的默认构造函数、带参构造函数或工厂方法创建Bean的实例。

CglibSubclassingInstantiationStrategy扩展了SimpleInstantiationStrategy,为需要进行方法注入的Bean提供了支持。它利用CGLib类库为Bean动态生成子类,在子类中生成方法注入的逻辑,然后使用这个动态生成的子类创建Bean的实例。

InstantiationStrategy仅负责实例化Bean的操作,相当于执行Java语言中new的功能,它不会参与Bean属性的设置工作。

3、BeanWrapper

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)使部署更简单

1、PropertyPlaceholderConfigurer属性文件

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属性文件

<bean class="com.smart.placeholder.EncryptPropertyPlaceholderConfigurer"
p:location="classpath:com/smart/placeholder/jdbc.properties"
p:fileEncoding="utf-8"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${driverClassName}"
p:url="${url}"
p:username="${userName}"
p:password="${password}" />
PropertyPlaceholderConfigurer的属性

locations:如果只有一个属性文件,直接使用location属性指定,如果有多个属性文件,则使用locations属性进行设置,可以像配置List一样配置locations属性。

fileEncoding:属性文件的编码格式。

order:如果配置文件中定义了多个PropertyPlaceholderConfigurer,则通过该属性指定优先顺序。

placeholderPrefix:通过${属性名}引用属性文件中的属性项,其中"${"为默认的占位符前缀,可以根据需要改为其他的前缀符。

placeholderSuffix:占位符后缀,默认为"}"。

使用<context:property-placeholder>引用属性文件:

<context:property-placeholder
location="classpath:com/smart/placeholder/jdbc.properties" file-encoding="utf8"/>
基于注解及基于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);
}
}


2、使用加密的属性文件:

PropertyPlaceholderConfigurer中的下列方法用于在属性使用前对属性进行转换:

(1)void convertProperties(Properties props):属性文件中的所有属性值都封装在props中,覆盖此方法,可以对所有的属性值进行转换处理。

(2)String convertProperty(String propertyName,String propertyValue):在加载属性文件并读取文件中的每个属性时,都会调用此方法进行转换处理。

(3)String convertPropertyValue(String originalValue):同上一个方法类似。

3、属性文件自身的引用:

Spring允许在属性文件中使用${propName}实现属性之间的相互引用。

三、引用Bean的属性值

在Spring3.0中,可以通过类似#{beanName.beanProp}的方式引用另一个Bean的值。在基于注解和基于Java类配置的Bean中,可以通过@Value("#{beanName.propName}")的注解形式引用Bean的属性值。

四、Spring容器事件

在事件体系中,处理事件和监听器外,还有3个重要的概念:
(1)事件源:事件的产生者
(2)事件监听器注册表:一个事件监听器注册到组件或框架中,其实就是保存在事件监听器注册表中。当组件和框架中的事件源产生事件时,就会通知这些位于事件监听器注册表的监听器。
(3)事件广播器:它是事件和事件监听器沟通的桥梁,负责把事件通知给事件监听器。如下图是事件体系的几个角色:



1、Spring事件类结构
如下图是事件类结构



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为事件广播器定义了接口,并提供了实现类,如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息