标签:struts2 struts2初始化 provider configuration packageprovider
在上一篇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
标签:struts2 struts2初始化 provider configuration packageprovider
原文地址:http://blog.csdn.net/a1969212650/article/details/34419115