首页 > 代码库 > struts2源码探索之初始化(四)
struts2源码探索之初始化(四)
在上一篇文章 struts2源码探索之初始化(三)中,已经分析到了创建bootstrap这个容器的最后一步了,调用ContainerBuilder类的create()。恩,接下来看这个方法:
public Container create(boolean loadSingletons) { ensureNotCreated(); created = true; final ContainerImpl container = new ContainerImpl( new HashMap<Key<?>, InternalFactory<?>>(factories)); if (loadSingletons) { container.callInContext(new ContainerImpl.ContextualCallable<Void>() { public Void call(InternalContext context) { for (InternalFactory<?> factory : singletonFactories) { factory.create(context); } return null; } }); } container.injectStatics(staticInjections); return container; }
先创建一个Container对象,也就是其实现类ContainerImpl啦,应注意的是ContainerBuilder收集的factories传递给了它,然后通过工厂创建所有scope指定为singleton的bean,最后对某些类(在这里,目前是空,也就是没什么注入操作)注入内置bean。大概就是这样子,接下来详细说明这段代码。
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> T inject(Class<T> implementation); /** * Gets an instance of the given dependency which was declared in * {@link com.opensymphony.xwork2.inject.ContainerBuilder}. */ <T> T getInstance(Class<T> type, String name); /** * Convenience method. Equivalent to {@code getInstance(type, * DEFAULT_NAME)}. */ <T> T getInstance(Class<T> 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<String> 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(); }
从接口的定义可以看出,Container有两种关键的操作,一是注入,也就是inject(),二是获得实例,也就是getInstance()。可还记得前面说过,要创建出一个可用的Container,需要多方收集bean配置?bean配置中的class属性指定的Factory,以及硬编码加入的诸多factory,都是用来创建bean对象的。也就是说这许许多多的factory都是为getInstance()方法服务的。而Container又为什么要创建bean呢?因为在处理请求或其他地方需要使用bean,并且这些bean可以提供给任何类,通过inject()这个方法。关于注入的更详细的分析,在后面给出。
2.ContainerImpl
先看它存储数据的属性:
class ContainerImpl implements Container { final Map<Key<?>, InternalFactory<?>> factories; final Map<Class<?>, Set<String>> factoryNamesByType; /** * Field and method injectors. */ final Map<Class<?>, List<Injector>> injectors = new ReferenceCache<Class<?>, List<Injector>>() { @Override protected List<Injector> create( Class<?> key ) { List<Injector> injectors = new ArrayList<Injector>(); addInjectors(key, injectors); return injectors; } }; }
可以看出,一是factoies,二是injectors,和Container的两种操作是一一对应的。
至此,也就完成了bootstrap这个容器的创建。所以,让我们回到Configuration类。这真是相当漫长的旅途啊。
希望大家还记得我们曾经走过的这段路:
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; }
bootstrap被创建出来之后,自然就是使用它了。用它来创建出最后的Container。
调用各个Provider的register()
下面就说说各个Provider的register()
1.FileManagerProvider
这个前面说过了,就不说了。
2.DefaultPropertiesProvider
加载default.properties文件的配置信息到ContainerProperties对象中。
3.XmlConfigurationProvider
加载xml配置文件(struts-default.xml,struts.xml,)中的<bean>和<constant>元素,bean就加入到factories,constant就加入到ContainerProperties,这些信息最终都由Container对象去维护。
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);
关于ActionContext,在处理请求时再详细讲吧。
先看接下来的代码:
for (final ContainerProvider containerProvider : providers) { if (containerProvider instanceof PackageProvider) { container.inject(containerProvider); ((PackageProvider)containerProvider).loadPackages(); packageProviders.add((PackageProvider)containerProvider); } }
一一调用loadPackage()方法来加载<package>元素,且看loadPackage():
public void loadPackages() throws ConfigurationException { List<Element> reloads = new ArrayList<Element>(); 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; }
不详细讲了,就是加载xml中的<package>,最终封装成PackageConfig类。
接下来是这段代码:
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); }
由于返回的packageProvidernames是没有元素的,所以也就不执行什么有意义的代码了。
再接下来就是:
rebuildRuntimeConfiguration();
前面说过,RuntimeConfiguration封装了Action配置,封装成了ActionConfig类,这里就是重新读取PackageConfig中的ActionConfig了。如果觉得有点迷茫,可以看看PackageConfig的属性:
public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator { private static final Logger LOG = LoggerFactory.getLogger(PackageConfig.class); protected Map<String, ActionConfig> actionConfigs; protected Map<String, ResultConfig> globalResultConfigs; protected Map<String, Object> interceptorConfigs; protected Map<String, ResultTypeConfig> resultTypeConfigs; protected List<ExceptionMappingConfig> globalExceptionMappingConfigs; protected List<PackageConfig> 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; }
可以看到,PackageConfig封装了ActionConfig,ResultConfig,ExceptionMappingConfig,InterceptorConfigs,以及namespace等等。是不是和<package>元素一一对应呢?哈哈。
到此为止,最终的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; }
Container已经创建好了,接下来就是把它保存起来。保存在ContainerHolder中。这里用到了ThreadLocal设计模式,且听我细细道来。
看ContainerHolder类的源代码:
class ContainerHolder { private static ThreadLocal<Container> instance = new ThreadLocal<Container>(); 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(); } }
关于ThreadLocal设计模式的详细,可以参阅我转载的一篇文章:正确理解ThreadLocal
接下来就看前面提到的注入了。看Dispatcher类的init()中,这么一句代码:
container.inject(this);
这句代码执行什么功能呢?我们先找到Dispatcher类的这样的语句:
/** * 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=http://www.mamicode.com/StrutsConstants.STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP, required=false)>
这些方法都是带@Inject注解的。通俗易懂的说,对于带这种注解的方法来说,当调用container.inject(dispatcher)方法时,以上方法会一一被调用,并且传递进去的参数就是Container中内置的bean或者常量。是不是相当神奇呢?
通过这种方法,Container中的bean就可以为任何类服务了。
ok,到此为止,Dispatcher的初始化也已经全部完成。这就意味着,本系列的文章到此就要告一段落了。
struts2框架的初始化写了四篇文章才算基本介绍完成,嘿嘿,有小小的成就感,毕竟,如此遥远的旅途啊。
虽然才刚刚起步,但是,还是会再接再厉,接下来就是struts2处理请求的过程了。这就比初始化还要复杂的多了,真是一个挑战呢。