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