在搭建struts2项目时如果在web.xml中不指定struts.xml文件的路径,struts2会默认到/WEB-INF/classes中寻找加载其配置文件的,但我就是想把struts的配置文件放到我指定的位置下,这时该如何处理呢?我做了以下实验:
先看一下项目的文件结构:
我把struts的配置文件放到了/WEB-INF/deploy/pms/app-config/struts-config/下,并且在web.xml中的配置如下:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>config</param-name>
<param-value>
struts-default.xml,struts-plugin.xml,/WEB-INF/deploy/pms/app-config/struts-config/struts.xml
</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
满怀信心的部署项目,并潇洒的启动tomcat,擦,不想看到的问题还是来了,打印信息如下:
为什么不能加载配置文件呢(保证配置文件是无误的),难道不能解析这个路径,那把配置文件放到src的指定文件夹下试试(因为它不是默认从src发布的文件夹下找嘛),
并且修改了web.xml
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>config</param-name>
<param-value>
struts-default.xml,struts-plugin.xml,config/struts.xml
</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
再次部署启动,居然没错了。郁闷。。。,难道我非得把配置文件放到src下吗,有点不甘心,看看你struts2的源码怎么读取文件的吧,从tomcat的打印信息中可以看到,加载配置文件的类是XmlConfigurationProvider
16:10:36,403 INFO ContextLoader:301 - Root WebApplicationContext: initialization completed in 2546 ms
16:10:36,622 INFO XmlConfigurationProvider:42 - Parsing configuration file [struts-default.xml]
16:10:36,763 INFO XmlConfigurationProvider:42 - Parsing configuration file [struts-plugin.xml]
16:10:36,809 INFO XmlConfigurationProvider:42 - Parsing configuration file [config/struts.xml]
16:10:37,059 INFO StrutsSpringObjectFactory:42 - Initializing Struts-Spring integration...
那就在struts2源码中直接找这个类,在这个类中搜了一下打印的日志信息“Unable to locate configuration files of the name”,找到这个信息是在XmlConfigurationProvider类的private List<Document> loadConfigurationFiles(String fileName, Element includeElement)方法中打印的,方法实现如下:
private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
List<Document> docs = new ArrayList<Document>();
List<Document> finalDocs = new ArrayList<Document>();
if (!includedFileNames.contains(fileName)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Loading action configurations from: " + fileName);
} includedFileNames.add(fileName); Iterator<URL> urls = null;
InputStream is = null; IOException ioException = null;
try {
urls = getConfigurationUrls(fileName);
} catch (IOException ex) {
ioException = ex;
} if (urls == null || !urls.hasNext()) {
if (errorIfMissing) {
throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
} else {
if (LOG.isInfoEnabled()) {
LOG.info("Unable to locate configuration files of the name "
+ fileName + ", skipping");
}
return docs;
}
} URL url = null;
while (urls.hasNext()) {
try {
url = urls.next();
is = fileManager.loadFile(url); InputSource in = new InputSource(is); in.setSystemId(url.toString()); docs.add(DomHelper.parse(in, dtdMappings));
} catch (XWorkException e) {
if (includeElement != null) {
throw new ConfigurationException("Unable to load " + url, e, includeElement);
} else {
throw new ConfigurationException("Unable to load " + url, e);
}
} catch (Exception e) {
throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOG.error("Unable to close input stream", e);
}
}
}
} //sort the documents, according to the "order" attribute
Collections.sort(docs, new Comparator<Document>() {
public int compare(Document doc1, Document doc2) {
return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
}
}); for (Document doc : docs) {
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 ("include".equals(nodeName)) {
String includeFileName = child.getAttribute("file");
if (includeFileName.indexOf('*') != -1) {
// handleWildCardIncludes(includeFileName, docs, child);
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Vector<String> wildcardMatches = wildcardFinder.findMatches();
for (String match : wildcardMatches) {
finalDocs.addAll(loadConfigurationFiles(match, child));
}
} else {
finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
finalDocs.add(doc);
loadedFileUrls.add(url.toString());
} if (LOG.isDebugEnabled()) {
LOG.debug("Loaded action configuration from: " + fileName);
}
}
return finalDocs;
}
在方法的16行,该方法又调用了getConfigurationUrls(filename)方法:
protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false);
}
水还真是深,在getConfigurationUrls(filename)方法中又调用了其他类的方法,ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false)方法如下:
public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException { AggregateIterator<URL> iterator = new AggregateIterator<URL>(); iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName)); if (!iterator.hasNext() || aggregate) {
iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
} if (!iterator.hasNext() || aggregate) {
ClassLoader cl = callingClass.getClassLoader(); if (cl != null) {
iterator.addEnumeration(cl.getResources(resourceName));
}
} if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) {
return getResources('/' + resourceName, callingClass, aggregate);
} return iterator;
}
也该到尽头了吧!!从代码中可以看出,struts加载的配置文件都是从类加载器加载.class文件的路径中去寻找的,把配置文件放到WEB-INF下,它当然加载不到了,除非你写成http://xxx/工程名+配置文件路径,否则只能把配置文件写到src下了,这点struts2设计的还真是让人有点郁闷!