Builder设计模式是一种创建型模式,可以将一个复杂对象的构建构成和它的表示区分开来,使得同样的构建过程可以创建出不同的表示。Builder模式的适用场景如下:
1、要将创建一个复杂对象的算法与组成这个对象的部分以及组成部分的组合方式相互独立的时候;
2、需要构建多种对象的表示时
Builder模式的基本结构如下
在Builder模式的结构组成中,Builder是一个接口,指定一个抽象的调用接口BuildPart,用来创建对象
ConcreteBuilder是一个具体的构造器类,在其中创建出构建产品所需要的组成部分,并且将它们进行组合,生产出具体的对象
Director通过调用Builder接口来创建对象。
Struts2中在创建容器(Container)对象的时候,就采用的是Builder模式来实现。Struts2中的容器是一个与具体的业务逻辑完全无关的编程元素,它的作用是用来管理相关对象的生命周期,可以通过容器获取对象的实例、处理对象之间的依赖关系。Builder模式在Struts2容器的创建过程中主要涉及到以下几个类和接口:ContainerBuilder、Container和ContainerImpl,它们之间的关系如下
这里将Builder模式中的Builder接口和实现类ConcreteBuilder结合到了一起,为ContainerBuilder。由于构建容器所需的参数很多,因此ContainerBuilder对象构建容器过程主要可以分成两步:
1、收集构建容器所需的参数
2、利用所收集的参数来构建容器。
构建ContainerImpl容器对象所需的参数主要是一系列的接口类型,名称和创建对象工厂的映射表,主要为
final
Map<Key<?>, InternalFactory<?>>
factories
;Key类维护了类信息、名词和哈希值。factories是构建容器所必需的,但其内容组成是与具体运行环境有关,既有来自于Struts2框架本身的配置所提供的一些组成部分,又有来自客户项目的配置数据提供的组成部分。
先提前说下,构造容器对象的时候的参数是一个个的键值对,Key中包含的是类的类型,名称和哈希值。InternalFactory代表的是生成类的对象的工厂对象,调用其中的factory方法,就可以生成相应的对象。
[收集构建容器所需参数]
ContainerBuilder提供了许多接口用于收集创建容器所需要的全部参数,具体表现是一些列重载的factory方法。如下所示
但其中一个方法是实现功能的主体,其它的factory方法主要是用来进行参数的适配和改造。这个主体factory方法就是图中标记为私有的factory方法,其代码如下
/**
* Maps a dependency.
* All methods in this class ultimately funnel through here.
*/
//the kernel working part
private
<T> ContainerBuilder factory(
final
Key<T> key,
InternalFactory<?
extends
T> factory, Scope scope) {
ensureNotCreated();
checkKey(key);
final
InternalFactory<?
extends
T> scopedFactory =
scope.scopeFactory(key.getType(), key.getName(), factory);
factories
.put(key, scopedFactory);
if
(scope == Scope.
SINGLETON
) {
singletonFactories
.add(
new
InternalFactory<T>() {
@Override
public
T create(InternalContext context) {
try
{
context.setExternalContext(ExternalContext. newInstance(
null
, key, context.getContainerImpl()));
return
scopedFactory.create(context);
}
finally
{
context.setExternalContext(
null
);
}
}
});
}
return
this
;
}
每次调用其它公共的factory方法,最后都会流向这里,通过factories.put(key,scopedFactory);来实现参数的收集过程。factories和singletonFactories都是ContainerBuilder的私有实例变量。Scope是一个枚举类(enum),用来实现对象的生命范围的控制。
也就是说,整个的容器参数初始化过程,其实就是一个不断网factories中添加相应的<key,factory>的过程。
最后当参数收集完成后,用收集到的factories来创建容器。
构造容器参数的收集过程在Struts2框架初始化的时候进行。
之前说过,Struts2框架的初始化入口来于项目的web.xml中配置的<filter>标签的子标签<filter-class>指定的类,一般是
<
filter-class
>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</
filter-class
>。在StrutsPrepareAndExecuteFilter的初始化方法init中,会初始化一个Dispatcher对象dispatcher,然后通过dispatcher.init();来实现这个Dispatcher对象的初始化。在这个init方法中,会加载很多和Struts2框架,以及应用相关的配置。构造容器所需的参数就始于此。init方法的部分代码如下所示
public
void
init() {
if
(
configurationManager
==
null
) {
configurationManager
= createConfigurationManager(BeanSelectionProvider.
DEFAULT_BEAN_NAME
);
}
try
{
init_FileManager();
//DefaultFileManager provides basic interface to access file on the File System and to monitor changes
init_DefaultProperties();
// [1] for loading struts.properties, getting the property key-value map, and property key-location map
init_TraditionalXmlConfigurations();
// [2] for loading struts.xml, struts-default.xml and struts-plugin.xml, or the user "config"
init_LegacyStrutsProperties();
// [3]
init_CustomConfigurationProviders();
// [5] loading custom configuration providers, set by "configProviders" label, spilt by ','
init_FilterInitParameters() ;
// [6] loading the filter parameters, set in web.xml
init_AliasStandardObjects() ;
// [7] setting the implementation for types, for the extension of the framework
Container container = init_PreloadConfiguration();
//usint inject to set the instance variables, calling funtions with "@Inject" annotation
container.inject(
this
);
首先对configurationManager对象进行初始化,然后是一系列的init_****形式的初始化方法的调用。接着就通过init_PreloadConfiguration获取了容器对象。收集构造容器参数的过程就在这一系列形如init_***的方法中实现。以init_FileManager()和init_DefaultProperties()为例来看。
private
void
init_FileManager()
throws
ClassNotFoundException {
if
(
initParams
.containsKey(StrutsConstants.
STRUTS_FILE_MANAGER
)) {
//从参数中获取用户指定的FileManager实现类的名称,并且加载类信息,保存在fileManagerClass变量中
configurationManager
.addContainerProvider(
new
FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName()));
}
else
{
//用户没有指定FileManager实现类,使用默认的文件管理类JBossFileManager
configurationManager
.addContainerProvider(
new
FileManagerProvider(JBossFileManager.
class
,
"jboss"
));
}
////下面部分省略,与上面类似。只不过是FileManagerFactory
}
通过configurationManager调用哪个addContainerProvider来收集构造容器所需参数。下面的init_DefaultProperties和init_LegacyStrutsProperties与上面类似。init_DefaultProperties是用来加载default.properties文件,获取相应的框架属性设置。
private
void
init_DefaultProperties() {
configurationManager
.addContainerProvider(
new
DefaultPropertiesProvider());
}
private
void
init_LegacyStrutsProperties() {
configurationManager
.addContainerProvider(
new
LegacyPropertiesConfigurationProvider());
}
收集到的参数会保存在configurationManager对象的containerProviders实例变量中,这个变量的定义如下
private
List<ContainerProvider>
containerProviders
=
new
CopyOnWriteArrayList<ContainerProvider>();
ContainerProvider是一个接口类,其中定义了公共接口函数register,用来向容器中注册属性和相应的参数,方法原型如下
public
void
register(ContainerBuilder builder, LocatableProperties props)
throws
ConfigurationException;
在各个ContainerProvider的实现类中,对register的实现时,通常会调用builder.factory方法。这样,相应的参数就被传递到了这个builder对象的factories属性中。
[利用收集的参数来构建容器]
接着之前的调用,收集参数结束后,
会到
init_PreloadConfiguration();
这个方法通过调用configurationManager对象的getConfiguration方法来获取配置对象configuration。
如果是首次由configuratonManager对象调用ConfiguratonManager类的getConfiguration方法,那么会走到configuraton.reloadContainer这里,并且把containerProviders作为参数传递进去,也就是初始化Struts2框架的时候保存收集的信息的成员ConfigurationManager类的成员变量containerProviders。
在configuration.reloadContainer方法中,调用createBootstrapContainer,并将之前的containerProviders参数传过去,在这里真正实现了容器的创建。
protected
Container createBootstrapContainer(List<ContainerProvider> providers) {
/*首先生成一个ContainerBuilder对象,然后还是之前收集到的参数的解析,调用register来实现对builder对象的factories成员变量的设置。前面有介绍*/
ContainerBuilder builder =
new
ContainerBuilder();
boolean
fmFactoryRegistered =
false
;
for
(ContainerProvider provider : providers) {
if
(provider
instanceof
FileManagerProvider) {
provider.register(builder,
null
);
}
if
(provider
instanceof
FileManagerFactoryProvider) {
provider.register(builder,
null
);
fmFactoryRegistered =
true
;
}
}
/*
然后是对框架的扩展点的实现类,完成框架功能必需,因此也需要加载,否则容器无法正常起效
*/
builder.factory(ObjectFactory.
class
, Scope.
SINGLETON
);
builder.factory(FileManager.
class
,
"system"
, DefaultFileManager.
class
, Scope.
SINGLETON
);
if
(!fmFactoryRegistered) {
builder.factory(FileManagerFactory.
class
, DefaultFileManagerFactory.
class
, Scope.
SINGLETON
);
}
builder.factory(ReflectionProvider.
class
, OgnlReflectionProvider.
class
, Scope.
SINGLETON
);
builder.factory(ValueStackFactory.
class
, OgnlValueStackFactory.
class
, Scope.
SINGLETON
);
builder.factory(XWorkConverter.
class
, Scope.
SINGLETON
);
builder.factory(ConversionPropertiesProcessor.
class
, DefaultConversionPropertiesProcessor.
class
, Scope.
SINGLETON
);
builder.factory(ConversionFileProcessor.
class
, DefaultConversionFileProcessor.
class
, Scope.
SINGLETON
);
builder.factory(ConversionAnnotationProcessor.
class
, DefaultConversionAnnotationProcessor.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverterCreator.
class
, DefaultTypeConverterCreator.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverterHolder.
class
, DefaultTypeConverterHolder.
class
, Scope.
SINGLETON
);
builder.factory(XWorkBasicConverter.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverter.
class
, XWorkConstants.
COLLECTION_CONVERTER
, CollectionConverter.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverter.
class
, XWorkConstants.
ARRAY_CONVERTER
, ArrayConverter.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverter.
class
, XWorkConstants.
DATE_CONVERTER
, DateConverter.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverter.
class
, XWorkConstants.
NUMBER_CONVERTER
, NumberConverter.
class
, Scope.
SINGLETON
);
builder.factory(TypeConverter.
class
, XWorkConstants.
STRING_CONVERTER
, StringConverter.
class
, Scope.
SINGLETON
);
builder.factory(TextProvider.
class
,
"system"
, DefaultTextProvider.
class
, Scope.
SINGLETON
);
builder.factory(ObjectTypeDeterminer.
class
, DefaultObjectTypeDeterminer.
class
, Scope.
SINGLETON
);
builder.factory(PropertyAccessor.
class
, CompoundRoot.
class
.getName(), CompoundRootAccessor.
class
, Scope.
SINGLETON
);
builder.factory(OgnlUtil.
class
, Scope.
SINGLETON
);
builder.constant(XWorkConstants.
DEV_MODE
,
"false"
);
builder.constant(XWorkConstants.
LOG_MISSING_PROPERTIES
,
"false"
);
builder.constant(XWorkConstants.
RELOAD_XML_CONFIGURATION
,
"false"
);
//参数收集完成,通过create实现容器的创建
return
builder.create(
true
);
}
在ContainerBuilder类的create实现中,主要就是利用前面收集到的factories来生成一个容器对象。create的源代码如下所示
public
Container create(
boolean
loadSingletons) {
ensureNotCreated();
created
=
true
;
final
ContainerImpl container =
new
ContainerImpl(
new
HashMap<Key<?>, InternalFactory<?>>(
factories
));
//whether or not to load singletons on Container creation
if
(loadSingletons) {
container.callInContext(
new
ContainerImpl.ContextualCallable<Void>() {
@Override
public
Void call(InternalContext context) {
for
(InternalFactory<?> factory :
singletonFactories
) {
factory.create(context);
}
return
null
;
}
});
}
container.injectStatics(
staticInjections
);
return
container;
}
注意其中的
final
ContainerImpl container =
new
ContainerImpl(
new
HashMap<Key<?>, InternalFactory<?>>(
factories
));
这句代码,这就是用收集到的参数生成容器的实现的地方。容器的实现类是ContainerImpl