最近在做架构方面的研究,采用模块化设计时,可能存在不同的模块jar包,都存在struts2的配置文件,此时如何自动加载jar包中的配置文件就成为一个问题。
// 初始化struts内部日志
init.initLogging(config);
//创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//初始化类属性:prepare 、execute
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
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);
}
虽然struts2提供通配符和action的自动加载,但是加载的是web-inf/classes中的action配置文件,默认是不能加载jar包中的(struts2自身jar包中的
"struts-default.xml,struts-plugin.xml,struts.xml",都是在classpath下的。)。如果我们将所有的配置文件存放在一个配置文件中,这样在项目间的协同开发和测试就会出现混乱,而最好的开发模式就是每个模块都有自己的action配置文件,而系统会自动搜索到所有的配置文件,采用自动加载的方式来完成struts2的初始化。
首先我们分析下源码,找出修改点:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
StrutsPrepareAndExecuteFilter的init方法
init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
try {
//封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig);
InitOperations init = new InitOperations();
try {
//封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config = new FilterHostConfig(filterConfig);
// 初始化struts内部日志
init.initLogging(config);
//创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//初始化类属性:prepare 、execute
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//回调空的postInit方法
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}
看下FilterHostConfig ,源码如下
public class FilterHostConfig implements HostConfig {
private FilterConfig config;
/**
*构造函数
*/
public FilterHostConfig(FilterConfig config) {
this.config = config;
}
/**
* 根据init-param配置的param-name获取param-value的值
*/
public String getInitParameter(String key) {
return config.getInitParameter(key);
}
/**
* 返回初始化参数名的List
*/
public Iterator<String> getInitParameterNames() {
return MakeIterator.convert(config.getInitParameterNames());
}
public ServletContext getServletContext() {
return config.getServletContext();
}
}
private FilterConfig config;
/**
*构造函数
*/
public FilterHostConfig(FilterConfig config) {
this.config = config;
}
/**
* 根据init-param配置的param-name获取param-value的值
*/
public String getInitParameter(String key) {
return config.getInitParameter(key);
}
/**
* 返回初始化参数名的List
*/
public Iterator<String> getInitParameterNames() {
return MakeIterator.convert(config.getInitParameterNames());
}
public ServletContext getServletContext() {
return config.getServletContext();
}
}
创建并初始化Dispatcher :
public Dispatcher initDispatcher( HostConfig filterConfig ) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
}
创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造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初始化,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml
/**
* Load configurations, including both XML and zero
-
configuration strategies,
* and update optional settings, including whether to reload configurations and resource files.
*/
public
void
init() {
if
(
configurationManager
==
null
) {
configurationManager
= createConfigurationManager(DefaultBeanSelectionProvider.
DEFAULT_BEAN_NAME
);
}
try
{
init_FileManager();
//加载org/apache/struts2/default.properties
init_DefaultProperties();
// [1]
init_TraditionalXmlConfigurations();
// [2] //加载struts-default.xml,struts-plugin.xml,struts.xml
init_LegacyStrutsProperties();
// [3]
init_CustomConfigurationProviders();
// [5] 加载自定义的配置
init_FilterInitParameters() ;
// [6] //Filter的初始化参数
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);
}
}
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);
}
}
}
}
从上面源码可以看出,只要配置
configProviders参数,并且实现了
ConfigurationProvider接口,struts2就会自动加载相应的配置。
下面的代码实现,主要借助于spring的PathMatchingResourcePatternResolver,自动扫描jar包中的文件,然后转换成URL的形式,继承getConfigurationUrls方法。
package com.XXX.xx.contrl.context;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.util.location.LocatableProperties;
/**
*
* @ClassName: CutomConfigurationProvider
* @Description:
* @author linhz
* @date 2014-8-14 下午05:38:25
*
*/
public class CutomConfigurationProvider extends XmlConfigurationProvider {
private static final String FILE_PATTERN = "classpath*:/conf/struts/plugins/**/struts-*.xml";
public CutomConfigurationProvider() {
Map<String, String> mappings = new HashMap<String, String>();
mappings.put(
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN",
"struts-2.0.dtd");
mappings.put(
"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN",
"struts-2.1.dtd");
mappings.put(
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN",
"struts-2.1.7.dtd");
mappings.put(
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN",
"struts-2.3.dtd");
setDtdMappings(mappings);
}
/*
* (non-Javadoc)
*
* @see com.opensymphony.xwork2.config.ContainerProvider#needsReload()
*/
@Override
public boolean needsReload() {
return true;
}
/*
* (non-Javadoc)
*
* @see
* com.opensymphony.xwork2.config.ContainerProvider#register(com.opensymphony
* .xwork2.inject.ContainerBuilder,
* com.opensymphony.xwork2.util.location.LocatableProperties)
*/
@Override
public void register(ContainerBuilder containerBuilder,
LocatableProperties props) throws ConfigurationException {
super.register(containerBuilder, props);
}
/*
* (non-Javadoc)
*
* @see com.opensymphony.xwork2.config.PackageProvider#loadPackages()
*/
@Override
public void loadPackages() throws ConfigurationException {
super.loadPackages();
}
@Override
protected Iterator<URL> getConfigurationUrls(String fileName)
throws IOException {
List<URL> urls = new ArrayList<URL>();
Resource[] resources = getAllResourcesUrl();
for (Resource resource : resources) {
urls.add(resource.getURL());
}
return urls.iterator();
}
/**
* 获取系统中需要搜寻的struts的配置
*
* @return
* @throws IOException
*/
private Resource[] getAllResourcesUrl() {
ResourcePatternResolver resoler = new PathMatchingResourcePatternResolver();
try {
return resoler.getResources(FILE_PATTERN);
} catch (IOException e) {
e.printStackTrace();
}
return new Resource[0];
}
}
然后在web.xml中添加如下的代码:
<!-- struts2 filter begin -->
<filter>
<filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param><param-name>configProviders</param-name><param-value>com.xxx.xx.contrl.context.CutomConfigurationProvider</param-value></init-param>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name><url-pattern>*.action</url-pattern>
</filter-mapping>
<!-- struts2 filter end -->