Struts2的Builder模式解析

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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值