在上一篇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():
欲知详情,且听下回分解。