码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-06-28 09:15:31      阅读:312      评论:0      收藏:0      [点我收藏+]

标签:struts2   struts2初始化   容器   container   containerbuilder   

在上一篇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():

欲知详情,且听下回分解。bubuko.com,布布扣

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

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

标签:struts2   struts2初始化   容器   container   containerbuilder   

原文地址:http://blog.csdn.net/a1969212650/article/details/34846899

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!