在上一篇文章 struts2源码探索之初始化(三)中,已经分析到了创建bootstrap这个容器的最后一步了,调用ContainerBuilder类的create()。恩,接下来看这个方法:
public Container create(boolean loadSingletons) {
ensureNotCreated();
created = true;
final ContainerImpl container = new ContainerImpl(
new HashMap, InternalFactory>(factories));
if (loadSingletons) {
container.callInContext(new ContainerImpl.ContextualCallable() {
public Void call(InternalContext context) {
for (InternalFactory factory : singletonFactories) {
factory.create(context);
}
return null;
}
});
}
container.injectStatics(staticInjections);
return container;
}
1.Container
前面已经多次提到了这个接口,但一直没有一探究竟,现在是时候了。看看接口的定义吧:
public interface Container extends Serializable {
/**
* Default dependency name.
*/
String DEFAULT_NAME = "default";
/**
* Injects dependencies into the fields and methods of an existing object.
*/
void inject(Object o);
/**
* Creates and injects a new instance of type {@code implementation}.
*/
T inject(Class implementation);
/**
* Gets an instance of the given dependency which was declared in
* {@link com.opensymphony.xwork2.inject.ContainerBuilder}.
*/
T getInstance(Class type, String name);
/**
* Convenience method. Equivalent to {@code getInstance(type,
* DEFAULT_NAME)}.
*/
T getInstance(Class type);
/**
* Gets a set of all registered names for the given type
* @param type The instance type
* @return A set of registered names or empty set if no instances are registered for that type
*/
Set getInstanceNames(Class type);
/**
* Sets the scope strategy for the current thread.
*/
void setScopeStrategy(Scope.Strategy scopeStrategy);
/**
* Removes the scope strategy for the current thread.
*/
void removeScopeStrategy();
}
2.ContainerImpl
先看它存储数据的属性:
class ContainerImpl implements Container {
final Map, InternalFactory> factories;
final Map, Set> factoryNamesByType;
/**
* Field and method injectors.
*/
final Map, List> injectors =
new ReferenceCache, List>() {
@Override
protected List create( Class key ) {
List injectors = new ArrayList();
addInjectors(key, injectors);
return injectors;
}
};
}
至此,也就完成了bootstrap这个容器的创建。所以,让我们回到Configuration类。这真是相当漫长的旅途啊。
希望大家还记得我们曾经走过的这段路:
public synchronized List reloadContainer(List providers) throws ConfigurationException {
packageContexts.clear();
loadedFileNames.clear();
List packageProviders = new ArrayList();
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() {
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 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;
}
调用各个Provider的register()
下面就说说各个Provider的register()
1.FileManagerProvider
这个前面说过了,就不说了。
2.DefaultPropertiesProvider
加载default.properties文件的配置信息到ContainerProperties对象中。
3.XmlConfigurationProvider
加载xml配置文件(struts-default.xml,struts.xml,)中的
4.LegacyPropertiesConfigurationProvider
加载properties文件,并且加入一个创建Local的InternalFactory。
5.FilterInitParameters的匿名Provider
加入filter的参数到ContainerProperties
6.BeanSelectionProvider
加入许多的bean factory和常量
public void register(ContainerBuilder builder, LocatableProperties props) {
alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
alias(FileManagerFactory.class, StrutsConstants.STRUTS_FILE_MANAGER_FACTORY, builder, props, Scope.SINGLETON);
alias(XWorkConverter.class, StrutsConstants.STRUTS_XWORKCONVERTER, builder, props);
alias(CollectionConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, builder, props);
alias(ArrayConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, builder, props);
alias(DateConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, builder, props);
alias(NumberConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, builder, props);
alias(StringConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, builder, props);
alias(ConversionPropertiesProcessor.class, StrutsConstants.STRUTS_CONVERTER_PROPERTIES_PROCESSOR, builder, props);
alias(ConversionFileProcessor.class, StrutsConstants.STRUTS_CONVERTER_FILE_PROCESSOR, builder, props);
alias(ConversionAnnotationProcessor.class, StrutsConstants.STRUTS_CONVERTER_ANNOTATION_PROCESSOR, builder, props);
alias(TypeConverterCreator.class, StrutsConstants.STRUTS_CONVERTER_CREATOR, builder, props);
alias(TypeConverterHolder.class, StrutsConstants.STRUTS_CONVERTER_HOLDER, builder, props);
alias(TextProvider.class, StrutsConstants.STRUTS_XWORKTEXTPROVIDER, builder, props, Scope.DEFAULT);
alias(LocaleProvider.class, StrutsConstants.STRUTS_LOCALE_PROVIDER, builder, props);
alias(ActionProxyFactory.class, StrutsConstants.STRUTS_ACTIONPROXYFACTORY, builder, props);
alias(ObjectTypeDeterminer.class, StrutsConstants.STRUTS_OBJECTTYPEDETERMINER, builder, props);
alias(ActionMapper.class, StrutsConstants.STRUTS_MAPPER_CLASS, builder, props);
alias(MultiPartRequest.class, StrutsConstants.STRUTS_MULTIPART_PARSER, builder, props, Scope.DEFAULT);
alias(FreemarkerManager.class, StrutsConstants.STRUTS_FREEMARKER_MANAGER_CLASSNAME, builder, props);
alias(VelocityManager.class, StrutsConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME, builder, props);
alias(UrlRenderer.class, StrutsConstants.STRUTS_URL_RENDERER, builder, props);
alias(ActionValidatorManager.class, StrutsConstants.STRUTS_ACTIONVALIDATORMANAGER, builder, props);
alias(ValueStackFactory.class, StrutsConstants.STRUTS_VALUESTACKFACTORY, builder, props);
alias(ReflectionProvider.class, StrutsConstants.STRUTS_REFLECTIONPROVIDER, builder, props);
alias(ReflectionContextFactory.class, StrutsConstants.STRUTS_REFLECTIONCONTEXTFACTORY, builder, props);
alias(PatternMatcher.class, StrutsConstants.STRUTS_PATTERNMATCHER, builder, props);
alias(StaticContentLoader.class, StrutsConstants.STRUTS_STATIC_CONTENT_LOADER, builder, props);
alias(UnknownHandlerManager.class, StrutsConstants.STRUTS_UNKNOWN_HANDLER_MANAGER, builder, props);
alias(UrlHelper.class, StrutsConstants.STRUTS_URL_HELPER, builder, props);
alias(TextParser.class, StrutsConstants.STRUTS_EXPRESSION_PARSER, builder, props);
if ("true".equalsIgnoreCase(props.getProperty(StrutsConstants.STRUTS_DEVMODE))) {
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, "true");
props.setProperty(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
props.setProperty(StrutsConstants.STRUTS_FREEMARKER_TEMPLATES_CACHE, "false");
props.setProperty(StrutsConstants.STRUTS_FREEMARKER_TEMPLATES_CACHE_UPDATE_DELAY, "0");
// Convert struts properties into ones that xwork expects
props.setProperty(XWorkConstants.DEV_MODE, "true");
} else {
props.setProperty(XWorkConstants.DEV_MODE, "false");
}
// Convert Struts properties into XWork properties
convertIfExist(props, StrutsConstants.STRUTS_LOG_MISSING_PROPERTIES, XWorkConstants.LOG_MISSING_PROPERTIES);
convertIfExist(props, StrutsConstants.STRUTS_ENABLE_OGNL_EXPRESSION_CACHE, XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE);
convertIfExist(props, StrutsConstants.STRUTS_ENABLE_OGNL_EVAL_EXPRESSION, XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION);
convertIfExist(props, StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, XWorkConstants.ALLOW_STATIC_METHOD_ACCESS);
convertIfExist(props, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, XWorkConstants.RELOAD_XML_CONFIGURATION);
LocalizedTextUtil.addDefaultResourceBundle("org/apache/struts2/struts-messages");
loadCustomResourceBundles(props);
}
Configuration Factory
各个Provider的register()方法加入了许多的factory,另外还加了创建Configuration的factory。
factory的收集完成之后,就是创建Container了,和上一个Container即bootstrap的创建过程是一样的,就不重复说了。
接着看reloadContainer(),已经到了这段代码:
container = builder.create(false);
setContext(container);
先看接下来的代码:
for (final ContainerProvider containerProvider : providers)
{
if (containerProvider instanceof PackageProvider) {
container.inject(containerProvider);
((PackageProvider)containerProvider).loadPackages();
packageProviders.add((PackageProvider)containerProvider);
}
}
public void loadPackages() throws ConfigurationException {
List reloads = new ArrayList();
verifyPackageStructure();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("package".equals(nodeName)) {
PackageConfig cfg = addPackage(child);
if (cfg.isNeedsRefresh()) {
reloads.add(child);
}
}
}
}
loadExtraConfiguration(doc);
}
if (reloads.size() > 0) {
reloadRequiredPackages(reloads);
}
for (Document doc : documents) {
loadExtraConfiguration(doc);
}
documents.clear();
declaredPackages.clear();
configuration = null;
}
接下来是这段代码:
Set 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();
public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
private static final Logger LOG = LoggerFactory.getLogger(PackageConfig.class);
protected Map actionConfigs;
protected Map globalResultConfigs;
protected Map interceptorConfigs;
protected Map resultTypeConfigs;
protected List globalExceptionMappingConfigs;
protected List parents;
protected String defaultInterceptorRef;
protected String defaultActionRef;
protected String defaultResultType;
protected String defaultClassRef;
protected String name;
protected String namespace = "";
protected boolean isAbstract = false;
protected boolean needsRefresh;
}
到此为止,最终的Container也构造出来了。
接下来干嘛呢?接下来又回到了Dispatcher的init()方法。想当初,我们是从以下这个方法开始渐行渐远的:
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;
}
看ContainerHolder类的源代码:
class ContainerHolder {
private static ThreadLocal instance = new ThreadLocal();
public static void store(Container instance) {
boolean reloadConfigs = Boolean.valueOf(instance.getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD));
if (!reloadConfigs) {
// reloadConfigs is false, configuration will do not change, just keep it
ContainerHolder.instance.set(instance);
}
}
public static Container get() {
return ContainerHolder.instance.get();
}
public static void clear() {
ContainerHolder.instance.remove();
}
}
接下来就看前面提到的注入了。看Dispatcher类的init()中,这么一句代码:
container.inject(this);
/**
* Modify state of StrutsConstants.STRUTS_DEVMODE setting.
* @param mode New setting
*/
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String mode) {
devMode = "true".equals(mode);
}
/**
* Modify state of StrutsConstants.DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP setting.
* @param disableRequestAttributeValueStackLookup New setting
*/
@Inject(value=StrutsConstants.STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP, required=false)
public void setDisableRequestAttributeValueStackLookup(String disableRequestAttributeValueStackLookup) {
this.disableRequestAttributeValueStackLookup = "true".equalsIgnoreCase(disableRequestAttributeValueStackLookup);
}
/**
* Modify state of StrutsConstants.STRUTS_LOCALE setting.
* @param val New setting
*/
@Inject(value=StrutsConstants.STRUTS_LOCALE, required=false)
public void setDefaultLocale(String val) {
defaultLocale = val;
}
/**
* Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
* @param val New setting
*/
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setDefaultEncoding(String val) {
defaultEncoding = val;
}
/**
* Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.
* @param val New setting
*/
@Inject(StrutsConstants.STRUTS_MULTIPART_SAVEDIR)
public void setMultipartSaveDir(String val) {
multipartSaveDir = val;
}
@Inject(StrutsConstants.STRUTS_MULTIPART_PARSER)
public void setMultipartHandler(String val) {
multipartHandlerName = val;
}
@Inject
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
this.valueStackFactory = valueStackFactory;
}
@Inject(StrutsConstants.STRUTS_HANDLE_EXCEPTION)
public void setHandleException(String handleException) {
this.handleException = Boolean.parseBoolean(handleException);
}
通俗易懂的说,对于带这种注解的方法来说,当调用container.inject(dispatcher)方法时,以上方法会一一被调用,并且传递进去的参数就是Container中内置的bean或者常量。是不是相当神奇呢?
通过这种方法,Container中的bean就可以为任何类服务了。
ok,到此为止,Dispatcher的初始化也已经全部完成。这就意味着,本系列的文章到此就要告一段落了。
struts2框架的初始化写了四篇文章才算基本介绍完成,嘿嘿,有小小的成就感,毕竟,如此遥远的旅途啊。
虽然才刚刚起步,但是,还是会再接再厉,接下来就是struts2处理请求的过程了。这就比初始化还要复杂的多了,真是一个挑战呢。