struts2源码学习之初始化(三)

在上一篇struts2源码学习之初始化(二)中已经详细介绍了Dispatcher的初始化工作,只差最后一点,容器的创建。这一篇就仔细介绍容器的创建过程,初始化过程以及容器的作用。还是先从源码入手,上一篇已经分析到了Dispatcher类的init()的这段代码:

Container container = init_PreloadConfiguration();
            container.inject(this);
            init_CheckWebLogicWorkaround(container);

接着分析,看看init_PreloadConfiguration()的实现:

private Container init_PreloadConfiguration() {
        Configuration config = configurationManager.getConfiguration();
        Container container = config.getContainer();

        boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
        LocalizedTextUtil.setReloadBundles(reloadi18n);

        ContainerHolder.store(container);

        return container;
    }

大致是获得一个Configuration对象,然后获得它持有的Container容器的引用,保存容器的引用到ContainerHolder类中。这么说可能有些抽象,下面细细分析。之前说过,Configuration对象是封装了所有配置信息的,有PackageConfig和Container属性。这里就是使用前面添加的诸多Provider来获得配置信息,然后创建一个Container。而ContainerHolder类则是使用了ThreadLocal设计模式来保存Container的引用,避免了线程安全的问题。这只是一个总述,下面详细分析。

先看ConfigurationManager类的getConfiguration()方法:

public synchronized Configuration getConfiguration() {
        if (configuration == null) {
            setConfiguration(createConfiguration(defaultFrameworkBeanName));
            try {
                configuration.reloadContainer(getContainerProviders());
            } catch (ConfigurationException e) {
                setConfiguration(null);
                throw new ConfigurationException("Unable to load configuration.", e);
            }
        } else {
            conditionalReload();
        }

        return configuration;
    }

代码还算比较容易懂,先创建一个Configuration,然后用它来重新加载一个Container。之前说过,ConfigurationManager类有个Configuration属性,并维护了保存PackageProvider和ContainerProvider两个List。这里就是创建Configuration对象了。看看Configuration是怎么被创建的:

protected Configuration createConfiguration(String beanName) {
        return new DefaultConfiguration(beanName);
    }

通过new一个Configuration的实现类DefaultConfiguration,关于DefaultConfiguration这个类上一篇已经对它有一个总的概览,这里详细看看它的reloadContainer(),它需要ConfigurationManager类的containerProviders,上一篇说过,ContainerProvider加载容器配置元素,最终用于创建容器,而这里就是创建容器的时候,所以自然是要使用它们了,且看代码:

public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
        packageContexts.clear();
        loadedFileNames.clear();
        List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();

        ContainerProperties props = new ContainerProperties();//封装配置文件中声明的常量
        ContainerBuilder builder = new ContainerBuilder();
        Container bootstrap = createBootstrapContainer(providers);
        for (final ContainerProvider containerProvider : providers)
        {
            bootstrap.inject(containerProvider);
            containerProvider.init(this);
            containerProvider.register(builder, props);
        }
        props.setConstants(builder);

        builder.factory(Configuration.class, new Factory<Configuration>() {
            public Configuration create(Context context) throws Exception {
                return DefaultConfiguration.this;
            }
        });

        ActionContext oldContext = ActionContext.getContext();
        try {
            // Set the bootstrap container for the purposes of factory creation

            setContext(bootstrap);
            container = builder.create(false);
            setContext(container);
            objectFactory = container.getInstance(ObjectFactory.class);

            // Process the configuration providers first
            for (final ContainerProvider containerProvider : providers)
            {
                if (containerProvider instanceof PackageProvider) {
                    container.inject(containerProvider);
                    ((PackageProvider)containerProvider).loadPackages();
                    packageProviders.add((PackageProvider)containerProvider);
                }
            }

            // Then process any package providers from the plugins
            Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
            for (String name : packageProviderNames) {
                PackageProvider provider = container.getInstance(PackageProvider.class, name);
                provider.init(this);
                provider.loadPackages();
                packageProviders.add(provider);
            }

            rebuildRuntimeConfiguration();
        } finally {
            if (oldContext == null) {
                ActionContext.setContext(null);
            }
        }
        return packageProviders;
    }

先总的说一下这个方法吧,先创建一个ContainerBuilder,然后创建一个bootstrap容器,这个容器并不是最终的容器,它只是一个过渡,它现在已经创建并保存了一些内置的bean了。容器为每个ContainerProvider注入需要的bean对象,然后Provider就可以调用本身的init()和register()注册到前面创建的ContainerBuilder中了。这时就可以创建出真正的Container对象了。然后就可以让每个PackageProvider来加载<package>配置信息了。

ok,这个方法中可说的地方是在太多了,且听我细细道来。

1.ContainerBuilder类:

public final class ContainerBuilder {

  final Map<Key<?>, InternalFactory<?>> factories =
      new HashMap<Key<?>, InternalFactory<?>>();

  final List<InternalFactory<?>> singletonFactories =
      new ArrayList<InternalFactory<?>>();

  final List<Class<?>> staticInjections = new ArrayList<Class<?>>();

  private static final InternalFactory<Container> CONTAINER_FACTORY =
      new InternalFactory<Container>() {
        public Container create(InternalContext context) {
          return context.getContainer();
        }
      };

  private static final InternalFactory<Logger> LOGGER_FACTORY =
      new InternalFactory<Logger>() {
        public Logger create(InternalContext context) {
          Member member = context.getExternalContext().getMember();
          return member == null ? Logger.getAnonymousLogger()
              : Logger.getLogger(member.getDeclaringClass().getName());
        }
      };

  /**
   * Constructs a new builder.
   */
  public ContainerBuilder() {
    factories.put(Key.newInstance(Container.class, Container.DEFAULT_NAME),
        CONTAINER_FACTORY);

    factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME),
        LOGGER_FACTORY);
  }

  /**
   * Maps a dependency. All methods in this class ultimately funnel through here.
   */
  private <T> ContainerBuilder factory(final Key<T> key,
      InternalFactory<? extends T> factory, Scope scope) 

  /**
   * Convenience method. Equivalent to {@code alias(type, Container.DEFAULT_NAME, type)}.
   */
  public <T> ContainerBuilder alias(Class<T> type, String alias)  

  /**
   * Maps a constant value to the given name.
   */
  public ContainerBuilder constant(String name, String value) {
    return constant(String.class, name, value);
  }

  /**
   * Upon creation, the {@link Container} will inject static fields and methods
   * into the given classes.
   */
  public ContainerBuilder injectStatics(Class<?>... types) {
    staticInjections.addAll(Arrays.asList(types));
    return this;
  }

  public Container create(boolean loadSingletons) 

}

以上代码列出了关键的属性和方法以及它们的注释。

先说方法,factory()和alias()有多个重载方法,用于收集创建Container对象的参数,也就是在ContainerProvider的register()中调用factory()方法了。然后调用这里的create()创建出一个Container来。重点要说一下的是,这里所用到的建造者模式。

因为Container对象的创建需要读取多个配置文件的配置信息,然后才能创建出可用的对象,如果直接new的话,创建出来的对象是不可用的。所以这里巧妙地使用了建造者模式来创建Container对象,零散的收集工作分布于各个ContainerProvider的register()中,只要一一调用register()就可以开始创建Container了。更多的关于建造者模式的介绍可以参阅原型模式与创建者模型

接下来要说的自然就是它的属性了。之前说过,Configuration类的Container属性将容器配置元素对象化了,而这些配置信息还是要暂时保存起来的,那么ContainerProvider类加载进来的配置信息保存在哪里呢?既然是通过ContainerBuilder类的factory()方法收集起来,自然也是保存在ContainerBuilder了。上面代码列出的factories属性和singletonFactories属性都是保存配置信息的。可以看到,factories是一个从Key到InternalFactory的Map,而singletonFactories则是一个保存InternalFactory的List。

接下来看看Key和InternalFactory为何物吧。

class Key<T> {

  final Class<T> type;
  final String name;
  final int hashCode;

  private Key(Class<T> type, String name) {
    if (type == null) {
      throw new NullPointerException("Type is null.");
    }
    if (name == null) {
      throw new NullPointerException("Name is null.");
    }

    this.type = type;
    this.name = name;

    hashCode = type.hashCode() * 31 + name.hashCode();
  }

  Class<T> getType() {
    return type;
  }

  String getName() {
    return name;
  }

  @Override
  public int hashCode() {
    return hashCode;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Key)) {
      return false;
    }
    if (o == this) {
      return true;
    }
    Key other = (Key) o;
    return name.equals(other.name) && type.equals(other.type);
  }

  @Override
  public String toString() {
    return "[type=" + type.getName() + ", name='" + name + "']";
  }

  static <T> Key<T> newInstance(Class<T> type, String name) {
    return new Key<T>(type, name);
  }
}

可以看到Key类相当简单了,就是封装了type和name两个对象。可能你会觉得没什么,但是当你打开struts-default.xml看到其中许许多多的bean的定义时,你就会惊讶地发现,bean的定义是这样的:

<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />

有type,有name,是的,你没有猜错,Key就是对应bean定义的type和name,那么bean元素的class属性又对应什么呢?自然是InternalFactory了。

好,看看InternalFactory,它被设计为接口:

interface InternalFactory<T> extends Serializable {

  /**
   * Creates an object to be injected.
   *
   * @param context of this injection
   * @return instance to be injected
   */
  T create(InternalContext context);
}

也就是说,bean元素被加载之后被解析了然后放入factories属性中。那么还有个singletonFactories呢?且看另一种bean的定义:

<bean type="com.opensymphony.xwork2.FileManager" class="com.opensymphony.xwork2.util.fs.DefaultFileManager" name="system" scope="singleton"/>
    

加了个scope属性,并制定为singleton,这样的bean自然是要被加入到singletonFactories了。两种bean的区别,想必也是容易猜出来的。

最后看看ContainerBuilder类的构造方法,可以看到加了两个内置的bean,它们并不是配置文件中定义的,而是以编码的方式加入的。这是两个什么样的bean呢?望文生义吧。

2.DefaultConfiguration类的createBootstrapContainer()

看看它的实现:

protected Container createBootstrapContainer(List<ContainerProvider> providers) {
        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.ENABLE_OGNL_EVAL_EXPRESSION, "false");
        builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
        return builder.create(true);
    }

代码似乎有点恐怖。总结起来,也就是通过ContainerBuilder创建一个Container的过程,其中ContainerBuilder收集FileManagerProvider的配置,然后又以硬编码的方式收集了多个bean对象以及几个常量,来看看这个过程。

我们只看FileManagerProvider的register()方法实现即可。其他Provider的register()类似。

public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
        builder.factory(FileManager.class, name, fileManagerClass, Scope.SINGLETON);
    }

看factory():

public <T> ContainerBuilder factory(final Class<T> type, final String name,
      final Class<? extends T> implementation, final Scope scope) {
    // This factory creates new instances of the given implementation.
    // We have to lazy load the constructor because the Container
    // hasn't been created yet.
    InternalFactory<? extends T> factory = new InternalFactory<T>() {

      volatile ContainerImpl.ConstructorInjector<? extends T> constructor;

      @SuppressWarnings("unchecked")
      public T create(InternalContext context) {
        if (constructor == null) {
          this.constructor =
              context.getContainerImpl().getConstructor(implementation);
        }
        return (T) constructor.construct(context, type);
      }

      @Override
      public String toString() {
        return new LinkedHashMap<String, Object>() {{
          put("type", type);
          put("name", name);
          put("implementation", implementation);
          put("scope", scope);
        }}.toString();
      }
    };

    return factory(Key.newInstance(type, name), factory, scope);
  }

该factory()最终调用以下factory():

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>() {
        public T create(InternalContext context) {
          try {
            context.setExternalContext(ExternalContext.newInstance(
                null, key, context.getContainerImpl()));
            return scopedFactory.create(context);
          } finally {
            context.setExternalContext(null);
          }
        }
      });
    }
    return this;
  }

其实,也就是加入到factories和singletonFactories的过程。可以看到,singletonFactories就是类似于快捷方式的作用,因为singletonFactories有的在factories都有了。

为什么要加入这么多的factory呢?后面说到Container的inject()就知道啦!

ok,收集完了配置信息,就可以调用create()创建出Container了。

3.ContainerBuilder的create():

欲知详情,且听下回分解。

struts2源码学习之初始化(三),布布扣,bubuko.com

时间: 2024-10-01 08:00:41

struts2源码学习之初始化(三)的相关文章

struts2源码学习之初始化(一)

看struts2源码已有一段时日,从今天开始,就做一个总结吧. 首先,先看看怎么调试struts2源码吧,主要是以下步骤: 使用Myeclipse创建一个web工程 导入struts2需要的jar包 如图: 让jar包关联源文件 在上图中的jar包右键,选择properties->java source attach,如果关联成功,双击jar包下的某个class文件就会显示java源代码了. 双击.class文件,在源代码关键地方设置断点 部署工程到Tomcat Tomcat以Debug方式启动

struts2源码学习之初始化(二)

在上一篇struts2源码学习之初始化(一)中,详细描述了StrutsPrepareAndExecuteFilter的init()的主要工作,这一篇就详细说说Dispatcher.从上一篇文章中,我们知道了Dispatcher在Filter的init()方法中被创建出来,那么,它的功能是什么呢?Dispatcher类的功能正如它的名字所示,是派发,派发请求. PrepareOperations类预处理请求,比如找到findActionMapping(),找到之后就要交给Dispatcher,让D

struts2源码探索之初始化(四)

在上一篇文章struts2源码探索之初始化(三)中,已经分析到了创建bootstrap这个容器的最后一步了,调用ContainerBuilder类的create().恩,接下来看这个方法: public Container create(boolean loadSingletons) { ensureNotCreated(); created = true; final ContainerImpl container = new ContainerImpl( new HashMap<Key<?

Java 源码学习系列(三)——Integer

Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的字段. 此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法. 类定义 public final class Integer extends Number implements Comparable<Integer> 从类定义中我们可以知道以下几点: 1.Integer类不能被继承 2.Integer类

Java集合源码学习笔记(三)LinkedList分析

前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的一种常用数据结构LinkedList的实现,LinkedList使用链表作为存储结构,链表是线性存储结构,在内存上不是连续的一段空间,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N),链表的特点是寻址困难,插入和删除容易.所有的代码都基于JDK 1.6. >>关于LinkedLis

jQuery源码学习2——初始化篇

这一篇主要总结一下jQuery这个js在引入的时候做的一些初始化工作 第一句window.undefined=window.undefined; 是为了兼容低版本的IE而写的 因为在低版本的IE中undefined不是window对象下的属性 因此window.undefined就是undefined 根据=运算符右结核性的特征,=右边的window.undefined就是undefined 既然window没有undefined属性 因此左边其实可以理解为在window下面扩展一个undefi

从Struts2源码学习Struts2的工作原理

今天我和我好基友啊斌通过探讨struts2的源码,总结了一下它的原理,代码是不会骗人的. 总的来说:struts的工作原理有7步: 1 客户端初始化一个指向Servlet容器的请求: 2 这个请求经过一系列的过滤器 在项目部署的时候,由tomcat容器读取项目的web.xml文件,测试的web.xml文件如下: <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5&quo

JDK源码学习--String篇(三) 存储篇

在进一步解读String类时,先了解下内存分配和数据存储的. 数据存储 1.寄存器:最快的存储区,位于处理器的内部.由于寄存器的数量有限,所以寄存器是按需分配. 2.堆栈:位于RAM中,但是通过堆栈指针可以从处理器哪里获得直接支持.堆栈指针向下移动,则分配新的内存:堆栈指针向上移动释放内存. 注:堆栈中存储基本的数据类型和[对象引用],但是Java对象存储在堆中. 3.堆:通用内存池,位于RAM中,用于存放所有的Java对象. 注:堆中存储的 new创建的对象和数组. 4.常量存储:存放常量.

Java项目源码学习笔记(三):PathMatcher

先来看看内部类Node节点: private static class Node{ private HashMap<String, Node> mMap; private int mKind = NOT_FOUND; Node addChild(String segment){ if(null == mMap){ mMap = new HashMap<String, Node>(); }else{ Node node = mMap.get(segment); if(node !=