-
Spring
作为现在最优秀的框架之一,已被广泛的使用,
51CTO
也曾经针对
Spring
框架中的
JDBC
应用
做过
报
道。本文将从另外一个视角试图剖析出
Spring
框架的作者设计
Spring
框架的骨骼架构的
设计理念,有那几个核
心组件?为什么需要这些组件?它们又是如何结合在一起构成
p>
Spring
的骨骼架构?
Spring<
/p>
的
AOP
特性又是如何
< br>利用这些基础的骨骼架构来工作的?
Spring
中又使
用了那些设计模式来完成它的这种设计的?它的这种
设计理<
/p>
念对对我们以后的软件设计有何启示?本文将详细解答这些问题。
Spring
的骨骼架构
Spring
总共有十几个组件,但是真正核心的组件只有几个,下面是<
/p>
Spring
框架的总体架构图:
图
框架的总
体架构图
从上图中可以看出
Spri
ng
框架中的核心组件只有三个:
Core
、
Context
和
Beans<
/p>
。
它们构建起了整个
Spring
的骨骼架构。没有它们就不可能有
AOP
、<
/p>
Web
等上层的特性功能。下面也将主要从这三个组件入手分析<
/p>
Spring
。
Spring
的设计理念
前面介绍了
Spring
的三个核心组件,如果再
在它们三个中选出核心的话,那就非
Beans
组件莫属了,为
何
这样说,其实
Spring
就是面向
Bean
的编程(
BOP,Bean
Oriented Programming
),
Bean
p>
在
Spring
中才是真正
的主角。
Bean
在
Spring
中作
用就像
Object
对
OOP
的意义一样,没有对象的概念就像没有面向对象编程,
Spring
p>
中
没有
Bean
也
就没有
Spring
存在的意义。就像一次演出舞台都准备好了
但是却没有演员一样。为什
么要
Be
an
这
种角色
Bean
或者为何在
Spring
如此重要,
< br>这由
Spring
框架的设计目标决定,
Spring
为何如此流行,
我们用
< br>Spring
的原因是什么,
想想你会发现原来
Spring
解决了一个非常关键的问题他可以让
你把对象之间的依赖关系转而用
配置文件来管理,也就是他
的依赖注入机制。而这个注入关系在一个叫
Ioc
容器中管理,
那
Ioc
容器中有又是什
么就是被
p>
Bean
包裹的对象。
Spring
正是通过把对象包装在
Bean
中而达到对
这些对象管理以及一些列额外操作
的目的。
< br>它这种设计策略完全类似于
Java
实现
OOP
的设计理念,
当然了
J
ava
本身的设计要比
Spring
复
杂太多太多,
但是都是构建一个数据结构,然后根据这个数据结构设计他的生存环境,并
让它在这个环境中
按照一定的规律
在
不停的运动,在它们的不停运动中设计一系列与环境或者与其他个体完成信息交换。这样想来回过头想想我们
p>
用到的其他框架都是大慨类似的设计理念。
核心组件如何协同工作
前面说
Bean
是
Spring
中关键因素,
那
Context
和
Core
又有何作用呢?前面吧
Bea
n
比作一场演出中的演员
的话,那
Co
ntext
就是这场演出的舞台背景,而
Core
应该就是演出的道具了。只有他们在一起才能
具备
能演出
一场好戏的最基本的条件。当然有最基本的条件还不能使这场演出脱颖而出,还要
他表演的节目足够的精彩,这
些节目就是
Spring
能提供的特色功能了。
我们知道
Bean
包装的是
Object
,
而
Object
必然有数据,<
/p>
如何给这些数据提供生存环境就是
Context
要解决
的问题,对
Context
来说他就是要发现每个
Bean
之间的关系,为它们建立这
种关系并且要维护好
这种关系。所
以
Context
就是一个
Bean
p>
关系的集合,这个关系集合又叫
Ioc
容器
,一旦建立起这个
Ioc
容器后
Spr
ing
就可以
为你工作了。那
Core
组件又有什么用武之地呢?其实
Core
就是发现、建立和维护每
个
Bea
n
之间的关系所需要
的一些列的工具,从这个角度看来,
Core
这个组件叫
Util
更能让你理解。
它们之间可以用下图来表示:
p>
图
2.
三个组件关系
核心组件详解
这里将详细介绍每
个组件内部类的层次关系,以及它们在运行时的时序顺序。我们在使用
Spring
p>
是应该注
意的地方。
Bean
组件
前面已经说明了
Bean
组件对
Sp
ring
的重要性,下面看看
Bean
这个组件式怎么设计的。
Bean
组件在
Spring
的
包下。这个包下的所
有类主要解决了三件事:
Bean
的定义、
Bean
的创建以及对
Bean
的解析。
对
Spring
的使用者来说
唯一需要关心的就是
Bean
的创建,
其他两个由
Spring
在内部帮你完成了,
< br>对你来说是透明的。
SpringBean
的创建时典型的工厂模式,他的顶级接口是
BeanFactory
,下图是这个工厂的继承层次关系:
<
/p>
图
工厂的继承关系
BeanFactory
有三个子类:
ListableBeanFactory
、
Hierar
chicalBeanFactory
和
Autowire
Capable Bean
Factory
。
但是从上图中我们可以发现最终的默认实现类是
DefaultListabl
eBeanFactory
,
他实
<
/p>
现了所有的接口。
那为何要定义这么多层次的接口呢?查阅这些接
口的源码和说明发现,每个接口都有他使用的场合,它主要是为
了区分在
Spring
内部在操作过程中对象的传递和转化过程中,对对象的
数据访问所做的限制。例如
ListableB
eanFactory
接口表示这些
Bean
< br>是可列表的,而
HierarchicalBeanFactory
表示的是这些
Bean
是有继
承关系的,也就是每个
Bean
有可能有父
< br>Bean
。
AutowireCapableBean
Factory
接口定义
Bean
的自
动装配规则。
这四个接口共同定义了
Bean
< br>的集合、
Bean
之间的关系、以及
Bean
行为。
Bean
的定义主要有
BeanDefinition
描述,如下图说明了这些类的层次关系:
< br>图
定义的类层次关系图
Bean
的定义就是完整的描述了在
Spri
ng
的配置文件中你定义的节点中所有的信息,包括各种子节点。当
Spring
成功解析你定义的一个节点后,在
Sprin
g
的内部他就被转化
成
BeanDefinition
对象。以后所有的操作
都是对这个对象完成的。
Bean
的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,
以应对可能的变化。
Bean
的解析主要就是对<
/p>
Spring
配置文件的解析。这个解析过程主要通过
下图中的类完成:
图
的解析类
当然还有具体对
tag
的解析这里并没
有列出。
Context
组件
Context
在
Spring
的
t
包下,前面已经讲解了
Cont
ext
组件在
Spring
中的作
p>
用,他实际上就是给
Spring
提供一个
运行时的环境,用以保存各个对象的状态。下面看一下这个
环境是如何构
建的。
ApplicationContext
是
Conte
xt
的顶级父类,
他除了能标识一个应用环境的基本信息外,<
/p>
他还继承了五个接
口,这五个接口主要是扩展了
< br>Context
的功能。下面是
Context
的类结构图:
图<
/p>
t
相关的类结构图
从上图中可以看出
ApplicationContext
继承了
BeanFactory
,这也说明了
< br>Spring
容器中运行的主体对象是
Bean
,另外
ApplicationContext
继承了
ResourceLoader
接口,使得
ApplicationContext
可以访
<
/p>
问到任何外部
资源,这将在
Core
p>
中详细说明。
ApplicationC
ontext
的子类主要包含两个方面:
ConfigurableApplicationContext
表示该
Context
是可修改的,
也就是在构建
p>
Context
中用户可以动态添加
或修改
已有的配置信息,它下面又有多个子类,其中最经常使用的是可更新的
Context<
/p>
,即
AbstractRefresh
ableApplicationContext
类。
WebApplicationContext
顾名思义,就是为
p>
web
准备的
Context
他可以直接访问到
ServletContext
,
通常情
况下,这个接口使用的少。
再
往下分就是按照构建
Context
的文件类型,接着就是访问
Context
的方式。这样一级一级构成了完整的
Context
等级层次。
总体来说
ApplicationContext
必须要完
成以下几件事:
◆标识一个应用环境
◆利用
BeanFactory
创建<
/p>
Bean
对象
◆保存对象关系表
◆能够捕获各种事件
Context
作为
Spring
的
< br>Ioc
容器,基本上整合了
Spring
的大部分功能,或者说是大部分功能的基础。
Core
组件
Core
组件作为
Spring
的核
心组件,他其中包含了很多的关键类,其中一个重要组成部分就是定义了资源的
访问方式
。这种把所有资源都抽象成一个接口的方式很值得在以后的设计中拿来学习。下面就
<
/p>
重要看一下这个
部分在
Spring
p>
的作用。
下图是
Resource
相关的类结构图:
图
ce
相关的类结构图
从上图可以看出
Resource
< br>接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。
对
资源的提供者来说,如何把资源包装起来交给其他人用这也是一个问题,我们看到
Res
ource
接口继承了
InputStreamSource
接口,
这个接口中有个
getInpu
tStream
方法,
返回的是
Inp
utStream
类。
这样所有的资源都
被可以通过
InputStream
这个类来获取,
所以也屏蔽了资源的提供者。
另外还有一
p>
个问题就是加载资源的问题,
也就是资源的加载者要统一,从上图中
可以看出这个任务是由
ResourceLoader
接口完成
,他屏蔽了所有的资源
加载者的差异,只需要实现这个接口就可以加载所有的资源,
p>
他的默认实现是
DefaultReso
urceLoader
。
下面看一下
Context
和
Resource<
/p>
是如何建立关系的?首先看一下他们的类关系图:
图
t
和
p>
Resource
的类关系图
从上图可以看出,
Context
是把资源的加载
、
解析和描述工作委托给了
ResourcePatternR
esolver
类来完成,
他相当于一个接头人,他把资源的加
载、解析和资源的定义整合在一起便于其他组件使用。
Core
组件中还有很
多类似的方式。
Ioc
容器如何工作
前面介绍了
Core
组件、
B
ean
组件和
Context
组件的结
构与相互关系,下面这里从使用者角度看一下他们是
如何运行的,以及我们如何让
Spring
完成各种功能,
Spring<
/p>
到底能有那些功能,这些功能是如
何得来的,下面
介绍。
如何创建
BeanFactory
工厂
正如图
2
描述的那样,<
/p>
Ioc
容器实际上就是
Context<
/p>
组件结合其他两个组件共同构建了一个
Bean
< br>关系网,
如
何构建这个关系网?构建的入口就在
AbstractApplicationContext
类的
refresh
方法中。
这个方
法的代码如下:
清单
h
1.
public void
refresh() throws BeansException,
IllegalStateException {
2.
3.
synchronized (pShutdownMonitor) {
4.
5.
//
Prepare this context for refreshing.
6.
7.
prepareRefresh();
8.
9.
//
Tell the subclass to refresh the internal bean
factory.
10.
11.
ConfigurableListableBeanFactory
beanFactory
=
obtainFreshBeanFactory
();
12.
13.
// Prepare the bean factory for use in this
context.
14.
15.
prepareBeanFactory(beanFactory);
16.
17.
try {
18.
19.
// Allows post- processing of the bean factory in
context subclasses.
20.
21.
postProcessBeanFactory(beanFactory);
22.
23.
// Invoke factory processors registered as beans
in& nbsp;the context.
24.
25.
invokeBeanFactoryPostProcessors(beanFactory);
26.
27.
// Register bean processors that intercept bean
crea tion.
28.
29.
registerBeanPostProcessors
(beanFactory);
30.
31.
// Initialize message source for this context.
32.
33.
initMessageSource();
34.
35.
// Initialize event multicaster for this context.
36.
37.
initApplicationEventMulticaster();
38.
39.
// Initialize other special beans in specific
contex t subclasses.
40.
41.
onRefresh();
42.
43.
// Check for listener beans and register them.
44.
45.
registerListeners();
46.
47.
// Instantiate all remaining (non-lazy-init)
singletons.
48.
49.
finishBeanFactoryInitialization (beanFactory);
50.
51.
// Last step: publish corresponding event.
52.
53.
finishRefresh();
54.
55.
}
56.
57.
catch (BeansException ex) {
58.
59.
// Destroy already created singletons to avoid
dangl ing resources.
60.
61.
destroyBeans();
62.
63.
// Reset 'active' flag.
64.
65.
cancelRefresh(ex);
66.
67.
// Propagate exception to caller.
68.
69.
throw ex;
70.
71.
}
72.
73.
}
74.
75.
}
76.
这个方法就是构建整个
Ioc
容器过程的完整的代码,了解了里面的每一行代码基本上就了解大部分
Spring
的原理和功能了。
这段代码主要包含这样几个步骤:
◆
构建
BeanFactory
,以便于产生所需的“演员”
p>
◆注册可能感兴趣的事件
◆创建
Bean
实例对象
◆触发被监听的事件
下面就结合代码分析这几个过程。
第
二三句就是在创建和配置
BeanFactory
。这里是
p>
refresh
也就是刷新配置,前面介绍了
Context
有可更新
的子类,
这
里正是实现这个功能,
当
BeanFactory
已存在是就更新,
如果没有就新创建。
下面是
更新
BeanFactory
的方法代码:
清单
2.
AbstractRefreshableApplicationContext.
refreshBeanFactory
1.
protected
final void refreshBeanFactory() throws
BeansException {
2.
3.
if
(hasBeanFactory()) {
4.
5.
destroyBeans();
6.
7.
closeBeanFactory();
8.
9.
}
10.
11.
try {
12.
13.
DefaultListableBeanFactory
beanFactory
=
createBeanFactory
();
14.
15.
ializationId(getId());
16.
17.
customizeBeanFactory(beanFactory);
18.
19.
loadBeanDefinitions(beanFactory);
20.
21.
synchronized (ctoryMonitor) {
22.
23.
ctory
= beanFactory;
24.
25.
}
26.
27.
}
28.
29.
catch
(IOException ex) {
30.
31.
throw
new ApplicationContextException(
32.
33.
34.
35.
+ getDisplayName (), ex);
36.
37.
}
38.
39.
}
40.
这个方法实现了
AbstractA
pplicationContext
的抽象方法
refres
hBeanFactory
,这段代码清楚的说明了
BeanF
actory
的创建过程。
注意
Bea
nFactory
对象的类型的变化,
前
面介绍了他有很多子类,
在什么情况下使用
不同的子类这非常关键。
BeanFactory
的原
始对象是
DefaultListableBeanFactory
,这个非常关键,因为他设计
到后面对这个对象的多种操作,下面看一下这个
类的继承层次类图:
图
tListableBeanFa
ctory
类继承关系图
从这个图中
发现除了
BeanFactory
相关的类外,
还发现了与
Bean
的
reg
ister
相关。
这在
refresh
BeanFactory
方法中有一行
loadBeanDef
initions(beanFactory)
将找到答案,这个方法将开始加载、解析
Bean
的定义,也就
是把用户定义
的数据结构转化为
Ioc
容器中的特定数据结构。
这个过程可以用下面时序图解释:
图
11.
创
建
BeanFactory
时序图
<
/p>
Bean
的解析和登记流程时序图如下:
图
12.
解
析和登记
Bean
对象时序图
创建好
BeanFactory
后,接下去添
加一些
Spring
本身需要的一些工具类,这个操作在
AbstractApplicationContext
的
prepareBeanFactory
方法完成。
AbstractApplicationContext
中接下来的三行代码对
Spring
的功能扩展性起了至关重
要的作用。前两行主
要是让你现在可以对已经构建的
BeanF
actory
的配置做修改,
后面一行就是让你可以对以后再<
/p>
创建
Bean
的实例
对象时添加一些自定义的操作。所以他们都是扩展了
Sp
ring
的功能,所以我们要学习使用
Spring
必须对这一部
分搞清楚。
其中在
invokeBeanFactoryPostProcessors
方法中主要是获取实现
BeanFactoryPostProcess
or
接口的子类。
并执行它的
post
ProcessBeanFactory
方法,这个方法的声明如下:
< br>
清单
ocessBeanFactory
1.
void pos
tProcessBeanFactory(ConfigurableListableBeanFactor
y beanFactory)
2.
3.
throws
BeansException;
它的参数是
beanFactory
,说明可以对
beanF
actory
做修改,这里注意这个
beanFactory<
/p>
是
ConfigurableListableBeanFact
ory
类型的,这也印证了前面介绍的不同
BeanFacto
ry
所使用的场合不同,这里
只能是
可配置的
BeanFactory
,防止一些数据被用户随意修
改。
registerBeanPostProcessor
s
方法也是可以获取用户定义的实现了
BeanPostPro
cessor
接口的子类,
并执行
把它
们注册到
BeanFactory
对象中的
beanPostProcessors
变量中。
Bean
PostProcessor
中声明
了两个方法:
postProcessBeforeInitialization
p>
、
postProcessAfterInitializatio
n
分别用于在
Bean
对象初始化时执
行。
可
以执行用户自定义的操作。
<
/p>
后面的几行代码是初始化监听事件和对系统的其他监听者的注册,
监听者必须是
ApplicationListener
的子<
/p>
类。
如何创建
Bean
实例并构建
Bean
的关系网
下面就是
Bean
< br>的实例化代码,是从
finishBeanFactoryInitializa
tion
方法开始的。
清单
BeanFactoryInitialization
1.
protected void
finishBeanFactoryInitialization(
2.
3.
ConfigurableListableBeanFactory beanFactory) {
4.
5.
6.
7.
// Stop using the temporary ClassLoader for type
matching.
8.
9.
pClassLoader(null);
10.
11.
12.
13.
// Allow for caching all bean definition metadata,
not expecting further changes
.
14.
15.
Configuration();
16.
17.
18.
19.
// Instantiate all remaining (non-lazy-init)
singletons.
20.
21.
tantiateSingletons();
22.
23.
}
24.
从上面代码中可以发现
Bean
p>
的实例化是在
BeanFactory
中发
生的。
preInstantiateSingletons
方
法的代码
如下:
清单
tantiateSingletons
1.
public void
preInstantiateSingletons() throws BeansException {
2.
3.
if (Enabled()) {
4.
5.
(
6.
7.
}
8.
9.
synchronized (finitionMap) {
10.
11.
for (String beanName : finitionNames) {
12.
13.
RootBeanDefinition
bd
=
getMergedLocalBeanDefinition
(bea
nName);
14.
15.
if (!ract() && leton()
16.
17.
&& !Init()) {
18.
19.
if (isFactoryBean(beanName)) {
20.
21.
final FactoryBean
factory
=
22.
23.
(FactoryBean) getBean(FACTORY_BEAN_PREFIX+
beanName);
24.
25.
boolean
isEagerInit;
26.
27.
if (urityManager() != null
28.
29.
&& factory instanceof SmartFactoryBean) {
30.
31.
isEagerInit
=
Acc
essController
.doPrivileged(
32.
33.
&nb sp; new
PrivilegedAction
()
{
34.
35.
&nb sp;
public Boolean run() {
36.
37.
return
((SmartFactoryBean) factory).isEagerInit();
38.
39.
&nb sp; }
40.
41.
}, getAcce
ssControlContext());
42.
43.
}
44.
45.
else {
46.
47.
isEagerInit
=
factory
instanceof
SmartFactoryBean
48.
49.
&nb sp; && ((SmartFactoryBean)
factory).isEagerInit();
50.
51.
}
52.
53.
if (isEagerInit) {
54.
55.
getBean (beanName);
56.
57.
}
58.
59.
}
60.
61.
else {
62.
63.
getBean(beanName);
64.
65.
}
66.
67.
}
68.
69.
}
70.
71.
}
72.
73.
}
74.
这里出现了一个非常重要的
Bean
—
—
FactoryBean
,
可以说<
/p>
Spring
一大半的扩展的功能都与这个
Bean
有关,
这是个特殊的
Bea
n
他是个工厂
Bean
,可以产生
p>
Bean
的
Bean
,这里的产生
Bean
是指
Bea
n
的实例,如果一个类
继承
Facto
ryBean
用户可以自己定义产生实例对象的方法只要实现他的
getObject
方法。然而在
Spring
内部这
个
Bean
的实例对
象是
FactoryBean
,通过调用这个对象的
getObject
方
法就能获取用户自定义产生的对象,从
而为
Spring
提供了很好的扩展性。
Spring
获取
p>
FactoryBean
本身的对象是在前面加上
< br>&
来完成的。
如何创建
Bean
的实例对象以及如何构建
Bean<
/p>
实例对象之间的关联关系式
Spring
中的一个核心关键,下面
是这个过程的流程图。
图
实例创建
流程图
如果是普通的
Bean
就直接创建他的实例,是通过调用
getBean
方法。下面是创建
Bean
实例的时序图:
-
-
-
-
-
-
-
-
-
上一篇:机车常用英文
下一篇:零售企业自有品牌战略分析