在上一篇struts2源码学习之初始化(一)中,详细描述了StrutsPrepareAndExecuteFilter的init()的主要工作,这一篇就详细说说Dispatcher。从上一篇文章中,我们知道了Dispatcher在Filter的init()方法中被创建出来,那么,它的功能是什么呢?Dispatcher类的功能正如它的名字所示,是派发,派发请求。
PrepareOperations类预处理请求,比如找到findActionMapping(),找到之后就要交给Dispatcher,让Dispatcher派发请求,这部分内容在struts2请求处理再详细说。现在先说Dispatcher的创建和初始化。
在Filter的init()中,Dispatcher的如下方法被调用:
public Dispatcher initDispatcher( HostConfig filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; }
private Dispatcher createDispatcher( HostConfig filterConfig ) { Map<String, String> params = new HashMap<String, String>(); for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) { String name = (String) e.next(); String value = filterConfig.getInitParameter(name); params.put(name, value); } return new Dispatcher(filterConfig.getServletContext(), params); }
关键的操作还是Dispatcher的init(),它做了大量的初始化工作。
public void init() { if (configurationManager == null) { configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } try { init_FileManager(); init_DefaultProperties(); // [1] init_TraditionalXmlConfigurations(); // [2] init_LegacyStrutsProperties(); // [3] init_CustomConfigurationProviders(); // [5] init_FilterInitParameters() ; // [6] init_AliasStandardObjects() ; // [7] Container container = init_PreloadConfiguration(); container.inject(this); init_CheckWebLogicWorkaround(container); if (!dispatcherListeners.isEmpty()) { for (DispatcherListener l : dispatcherListeners) { l.dispatcherInitialized(this); } } } catch (Exception ex) { if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex); throw new StrutsException(ex); } }
从以上代码可以归纳出,Dispatcher的init()做了如下的工作:
1.创建一个ConfigurationManager对象
这个ConfigurationManager类从它的类名就可以看出类的功能:管理配置信息。它是配置元素的操作接口,我们可以看看它的部分源码:
/** * ConfigurationManager - central for XWork Configuration management, including its ConfigurationProvider. */ public class ConfigurationManager { protected Configuration configuration; private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>(); private List<PackageProvider> packageProviders = new CopyOnWriteArrayList<PackageProvider>(); public ConfigurationManager() public ConfigurationManager(String name) public synchronized Configuration getConfiguration() protected Configuration createConfiguration(String beanName) public synchronized void setConfiguration(Configuration configuration) public List<ContainerProvider> getContainerProviders() public void setContainerProviders(List<ContainerProvider> containerProviders) public void addContainerProvider(ContainerProvider provider) public void clearContainerProviders() private void clearContainerProvider(ContainerProvider containerProvider) public synchronized void destroyConfiguration() public synchronized void conditionalReload() private void updateReloadConfigsFlag() private boolean needReloadPackageProviders() private boolean needReloadContainerProviders(List<ContainerProvider> providers) private void reloadProviders(List<ContainerProvider> providers) public synchronized void reload() }
在ConfigurationManager的代码中,我们看到它维护了一个Configuration的引用,以及PackageProvider和ContainerProvider的链表。Configuration中存储着配置信息,而ConfigurationManager则是对配置信息的操作,这正是将数据的存储和操作分离的设计思想了。
好,接下来我们分析一下Configuration,PackageProvider和ContainerProvider。
先说Configuration,它被设计为一个接口,接口的定义如下:
public interface Configuration extends Serializable { void rebuildRuntimeConfiguration(); PackageConfig getPackageConfig(String name); Set<String> getPackageConfigNames(); Map<String, PackageConfig> getPackageConfigs(); /** * The current runtime configuration. Currently, if changes have been made to the Configuration since the last * time buildRuntimeConfiguration() was called, you'll need to make sure to. * * @return the current runtime configuration */ RuntimeConfiguration getRuntimeConfiguration(); void addPackageConfig(String name, PackageConfig packageConfig); PackageConfig removePackageConfig(String packageName); void destroy(); @Deprecated void reload(List<ConfigurationProvider> providers) throws ConfigurationException; List<PackageProvider> reloadContainer(List<ContainerProvider> containerProviders) throws ConfigurationException; Container getContainer(); Set<String> getLoadedFileNames(); List<UnknownHandlerConfig> getUnknownHandlerStack(); void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack); }
ok,接着说Configuration,接口只是定义了行为,数据的存储还需要数据结构,这就在实现类了。Configuration只有一个实现类:DefaultConfiguration,其代码较多,我们只看两种配置信息是如何被存储的:
public class DefaultConfiguration implements Configuration { protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>(); protected RuntimeConfiguration runtimeConfiguration; protected Container container; protected String defaultFrameworkBeanName; protected Set<String> loadedFileNames = new TreeSet<String>(); protected List<UnknownHandlerConfig> unknownHandlerStack; ObjectFactory objectFactory; }
接着说PackageProvider,它也被设计为接口,接口定义如下:
public interface PackageProvider { public void init(Configuration configuration) throws ConfigurationException; public boolean needsReload(); public void loadPackages() throws ConfigurationException; }
再看ContainerProvider:
/** * Provides beans and constants/properties for the Container */ public interface ContainerProvider { public void destroy(); /** * Initializes with the configuration */ public void init(Configuration configuration) throws ConfigurationException; public boolean needsReload(); /** * Registers beans and properties for the Container */ public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException; }
以上两个接口被统一为一个ConfigurationProvider接口:
/** * Interface to be implemented by all forms of XWork configuration classes. */ public interface ConfigurationProvider extends ContainerProvider, PackageProvider { }
关于这个接口的实现类,本篇后面部分会详细说明的。
2.往ConfigurationManager添加一个FileManagerProvider
FileManagerProvider是干嘛的呢?自然是提供FileManager的了,从名字就可以看出来。关键是FileManager又是干嘛的,又如何提供。看看代码就知道了,在Dispatcher的init()中,调用了init_FileManager()方法:
private void init_FileManager() throws ClassNotFoundException { if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER)) {//Filter参数有没有struts.fileManager,一般情况下是没有 final String fileManagerClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER); final Class<FileManager> fileManagerClass = (Class<FileManager>) Class.forName(fileManagerClassName); if (LOG.isInfoEnabled()) { LOG.info("Custom FileManager specified: #0", fileManagerClassName); } configurationManager.addContainerProvider(new FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName())); } else { // add any other Struts 2 provided implementations of FileManager configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss")); } if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)) {//Filter参数有没有struts.fileManagerFactory,一般没有 final String fileManagerFactoryClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY); final Class<FileManagerFactory> fileManagerFactoryClass = (Class<FileManagerFactory>) Class.forName(fileManagerFactoryClassName); if (LOG.isInfoEnabled()) { LOG.info("Custom FileManagerFactory specified: #0", fileManagerFactoryClassName); } configurationManager.addContainerProvider(new FileManagerFactoryProvider(fileManagerFactoryClass)); } }
configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss"));这一条分支语句,也就是一个FileManagerProvider对象被加入到了List中。我们看看FileManagerProvider:
public class FileManagerProvider implements ContainerProvider { private Class<? extends FileManager> fileManagerClass; private String name; public FileManagerProvider(Class<? extends FileManager> fileManagerClass, String name) { this.fileManagerClass = fileManagerClass; this.name = name; } public void destroy() { } public void init(Configuration configuration) throws ConfigurationException { } public boolean needsReload() { return false; } public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { builder.factory(FileManager.class, name, fileManagerClass, Scope.SINGLETON); } }
/** * Basic interface to access file on the File System and to monitor changes */ public interface FileManager { /** * Enables configs reloading when config file changed * * @param reloadingConfigs {@link XWorkConstants#RELOAD_XML_CONFIGURATION} */ void setReloadingConfigs(boolean reloadingConfigs); /** * Checks if given file changed and must be reloaded if {@link #setReloadingConfigs(boolean)} is true * * @param fileName to check * @return true if file changed */ boolean fileNeedsReloading(String fileName); /** * Checks if file represented by provided URL should be reloaded * * @param fileUrl url to a file * @return true if file exists and should be reloaded, if url is null return false */ boolean fileNeedsReloading(URL fileUrl); /** * Loads opens the named file and returns the InputStream * * @param fileUrl - the URL of the file to open * @return an InputStream of the file contents or null * @throws IllegalArgumentException if there is no file with the given file name */ InputStream loadFile(URL fileUrl); /** * Adds file to list of monitored files if {@link #setReloadingConfigs(boolean)} is true * * @param fileUrl {@link URL} to file to be monitored */ void monitorFile(URL fileUrl); /** * Convert URLs to URLs with "file" protocol * @param url URL to convert to a jar url * @return a URL to a file, or null if the URL external form cannot be parsed */ URL normalizeToFileProtocol(URL url); /** * Indicate if given implementation supports current OS File System * * @return true if supports current OS File System */ boolean support(); /** * User's implementation should return false as then it will be taken in first place * * @return true if it's a framework provided implementation */ boolean internal(); Collection<? extends URL> getAllPhysicalUrls(URL url) throws IOException; }
ok,总结一下,就是init_FileManager的功能就是往configurationManager的List<ContainerProvider>属性添加一个元素。接下来的工作都类似了。所有添加Provider的工作都是为了最终创建出一个可用的Container(到时创建Container时,会一一调用这些provider的register()),以及对象化的事件映射配置元素。因为Provider就是provide的功能嘛。
3.往ConfigurationManager添加一个DefaultPropertiesProvider
和上面的类似,所以直接看源代码吧:
private void init_DefaultProperties() { configurationManager.addContainerProvider(new DefaultPropertiesProvider()); }
/** * Loads the default properties, separate from the usual struts.properties loading */ public class DefaultPropertiesProvider extends LegacyPropertiesConfigurationProvider { public void destroy() { } public void init(Configuration configuration) throws ConfigurationException { } public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings("org/apache/struts2/default"); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e); } loadSettings(props, defaultSettings); } }
public class LegacyPropertiesConfigurationProvider implements ConfigurationProvider { /** * The Logging instance for this class. */ private static final Logger LOG = LoggerFactory.getLogger(LegacyPropertiesConfigurationProvider.class); public void destroy() { Settings.reset(); } public void init(Configuration configuration) throws ConfigurationException { Settings.reset(); } public void loadPackages() throws ConfigurationException { } public boolean needsReload() { return false; } public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { final Settings settings = Settings.getInstance(); loadSettings(props, settings); // Set default locale by lazily resolving the locale property as needed into a Locale object builder.factory(Locale.class, new Factory<Locale>() { private Locale locale; public synchronized Locale create(Context context) throws Exception { if (locale == null) { String loc = context.getContainer().getInstance(String.class, StrutsConstants.STRUTS_LOCALE); if (loc != null) { StringTokenizer localeTokens = new StringTokenizer(loc, "_"); String lang = null; String country = null; if (localeTokens.hasMoreTokens()) { lang = localeTokens.nextToken(); } if (localeTokens.hasMoreTokens()) { country = localeTokens.nextToken(); } locale = new Locale(lang, country); } else { if (LOG.isInfoEnabled()) { LOG.info("No locale define, substituting the default VM locale"); } locale = Locale.getDefault(); } } return locale; } }); } /** * @param props * @param settings */ protected void loadSettings(LocatableProperties props, final Settings settings) { // We are calling the impl methods to get around the single instance of Settings that is expected for (Iterator i = settings.listImpl(); i.hasNext(); ) { String name = (String) i.next(); props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name)); } } }
4.往ConfigurationManager添加一个StrutsXmlConfigurationProvider
还是添加Provider:
private void init_TraditionalXmlConfigurations() { String configPaths = initParams.get("config"); if (configPaths == null) { configPaths = DEFAULT_CONFIGURATION_PATHS;//被定义为:struts-default.xml,struts-plugin.xml,struts.xml } String[] files = configPaths.split("\\s*[,]\\s*"); for (String file : files) { if (file.endsWith(".xml")) { if ("xwork.xml".equals(file)) { configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false)); } else { configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException("Invalid configuration file name"); } } }
5.往ConfigurationManager添加一个LegacyPropertiesConfigurationProvider
直接上代码吧:
private void init_LegacyStrutsProperties() { configurationManager.addContainerProvider(new LegacyPropertiesConfigurationProvider()); }
6.往ConfigurationManager添加用户自定义的xmlprovider
看看代码:
private void init_CustomConfigurationProviders() { String configProvs = initParams.get("configProviders"); if (configProvs != null) { String[] classes = configProvs.split("\\s*[,]\\s*"); for (String cname : classes) { try { Class cls = ClassLoaderUtil.loadClass(cname, this.getClass()); ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance(); configurationManager.addContainerProvider(prov); } catch (InstantiationException e) { throw new ConfigurationException("Unable to instantiate provider: "+cname, e); } catch (IllegalAccessException e) { throw new ConfigurationException("Unable to access provider: "+cname, e); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to locate provider class: "+cname, e); } } } }
7.往ConfigurationManager添加一个ConfigurationProvider实现类用于解析Filter参数
看看代码:
private void init_FilterInitParameters() { configurationManager.addContainerProvider(new ConfigurationProvider() { public void destroy() { } public void init(Configuration configuration) throws ConfigurationException { } public void loadPackages() throws ConfigurationException { } public boolean needsReload() { return false; } public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { props.putAll(initParams); } }); }
8.往ConfigurationManager添加一个BeanSelectionProvider
还是看看代码:
private void init_AliasStandardObjects() { configurationManager.addContainerProvider(new BeanSelectionProvider()); }
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); }
9.创建并初始化容器
文章篇幅似乎已经很长,这部分又比较重要,所以重新开一篇文章吧。
struts2源码学习之初始化(二),布布扣,bubuko.com
原文:http://blog.csdn.net/a1969212650/article/details/34419115