在上一篇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 params = new HashMap();
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 containerProviders = new CopyOnWriteArrayList();
private List packageProviders = new CopyOnWriteArrayList();
public ConfigurationManager()
public ConfigurationManager(String name)
public synchronized Configuration getConfiguration()
protected Configuration createConfiguration(String beanName)
public synchronized void setConfiguration(Configuration configuration)
public List getContainerProviders()
public void setContainerProviders(List 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 providers)
private void reloadProviders(List 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 getPackageConfigNames();
Map 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 providers) throws ConfigurationException;
List reloadContainer(List containerProviders) throws ConfigurationException;
Container getContainer();
Set getLoadedFileNames();
List getUnknownHandlerStack();
void setUnknownHandlerStack(List unknownHandlerStack);
}
ok,接着说Configuration,接口只是定义了行为,数据的存储还需要数据结构,这就在实现类了。Configuration只有一个实现类:DefaultConfiguration,其代码较多,我们只看两种配置信息是如何被存储的:
public class DefaultConfiguration implements Configuration {
protected Map packageContexts = new LinkedHashMap();
protected RuntimeConfiguration runtimeConfiguration;
protected Container container;
protected String defaultFrameworkBeanName;
protected Set loadedFileNames = new TreeSet();
protected List 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 fileManagerClass = (Class) 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 fileManagerFactoryClass = (Class) 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
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() {
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源码学习之初始化(三)