在搭建struts2项目时如果在web.xml中不指定struts.xml文件的路径,struts2会默认到/WEB-INF/classes中寻找加载其配置文件的,但我就是想把struts的配置文件放到我指定的位置下,这时该如何处理呢?我做了以下实验:
先看一下项目的文件结构:
我把struts的配置文件放到了/WEB-INF/deploy/pms/app-config/struts-config/下,并且在web.xml中的配置如下:
1 <filter> 2 <filter-name>struts2</filter-name> 3 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 4 <init-param> 5 <param-name>config</param-name> 6 <param-value> 7 struts-default.xml,struts-plugin.xml,/WEB-INF/deploy/pms/app-config/struts-config/struts.xml 8 </param-value> 9 </init-param> 10 </filter> 11 12 <filter-mapping> 13 <filter-name>struts2</filter-name> 14 <url-pattern>/*</url-pattern> 15 </filter-mapping>
满怀信心的部署项目,并潇洒的启动tomcat,擦,不想看到的问题还是来了,打印信息如下:
为什么不能加载配置文件呢(保证配置文件是无误的),难道不能解析这个路径,那把配置文件放到src的指定文件夹下试试(因为它不是默认从src发布的文件夹下找嘛),
并且修改了web.xml
1 <filter> 2 <filter-name>struts2</filter-name> 3 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 4 <init-param> 5 <param-name>config</param-name> 6 <param-value> 7 struts-default.xml,struts-plugin.xml,config/struts.xml 8 </param-value> 9 </init-param> 10 </filter> 11 12 <filter-mapping> 13 <filter-name>struts2</filter-name> 14 <url-pattern>/*</url-pattern> 15 </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)方法中打印的,方法实现如下:
1 private List<Document> loadConfigurationFiles(String fileName, Element includeElement) { 2 List<Document> docs = new ArrayList<Document>(); 3 List<Document> finalDocs = new ArrayList<Document>(); 4 if (!includedFileNames.contains(fileName)) { 5 if (LOG.isDebugEnabled()) { 6 LOG.debug("Loading action configurations from: " + fileName); 7 } 8 9 includedFileNames.add(fileName); 10 11 Iterator<URL> urls = null; 12 InputStream is = null; 13 14 IOException ioException = null; 15 try { 16 urls = getConfigurationUrls(fileName); 17 } catch (IOException ex) { 18 ioException = ex; 19 } 20 21 if (urls == null || !urls.hasNext()) { 22 if (errorIfMissing) { 23 throw new ConfigurationException("Could not open files of the name " + fileName, ioException); 24 } else { 25 if (LOG.isInfoEnabled()) { 26 LOG.info("Unable to locate configuration files of the name " 27 + fileName + ", skipping"); 28 } 29 return docs; 30 } 31 } 32 33 URL url = null; 34 while (urls.hasNext()) { 35 try { 36 url = urls.next(); 37 is = fileManager.loadFile(url); 38 39 InputSource in = new InputSource(is); 40 41 in.setSystemId(url.toString()); 42 43 docs.add(DomHelper.parse(in, dtdMappings)); 44 } catch (XWorkException e) { 45 if (includeElement != null) { 46 throw new ConfigurationException("Unable to load " + url, e, includeElement); 47 } else { 48 throw new ConfigurationException("Unable to load " + url, e); 49 } 50 } catch (Exception e) { 51 throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement); 52 } finally { 53 if (is != null) { 54 try { 55 is.close(); 56 } catch (IOException e) { 57 LOG.error("Unable to close input stream", e); 58 } 59 } 60 } 61 } 62 63 //sort the documents, according to the "order" attribute 64 Collections.sort(docs, new Comparator<Document>() { 65 public int compare(Document doc1, Document doc2) { 66 return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2)); 67 } 68 }); 69 70 for (Document doc : docs) { 71 Element rootElement = doc.getDocumentElement(); 72 NodeList children = rootElement.getChildNodes(); 73 int childSize = children.getLength(); 74 75 for (int i = 0; i < childSize; i++) { 76 Node childNode = children.item(i); 77 78 if (childNode instanceof Element) { 79 Element child = (Element) childNode; 80 81 final String nodeName = child.getNodeName(); 82 83 if ("include".equals(nodeName)) { 84 String includeFileName = child.getAttribute("file"); 85 if (includeFileName.indexOf('*') != -1) { 86 // handleWildCardIncludes(includeFileName, docs, child); 87 ClassPathFinder wildcardFinder = new ClassPathFinder(); 88 wildcardFinder.setPattern(includeFileName); 89 Vector<String> wildcardMatches = wildcardFinder.findMatches(); 90 for (String match : wildcardMatches) { 91 finalDocs.addAll(loadConfigurationFiles(match, child)); 92 } 93 } else { 94 finalDocs.addAll(loadConfigurationFiles(includeFileName, child)); 95 } 96 } 97 } 98 } 99 finalDocs.add(doc); 100 loadedFileUrls.add(url.toString()); 101 } 102 103 if (LOG.isDebugEnabled()) { 104 LOG.debug("Loaded action configuration from: " + fileName); 105 } 106 } 107 return finalDocs; 108 }
在方法的16行,该方法又调用了getConfigurationUrls(filename)方法:
1 protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException { 2 return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false); 3 }
水还真是深,在getConfigurationUrls(filename)方法中又调用了其他类的方法,ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false)方法如下:
1 public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException { 2 3 AggregateIterator<URL> iterator = new AggregateIterator<URL>(); 4 5 iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName)); 6 7 if (!iterator.hasNext() || aggregate) { 8 iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName)); 9 } 10 11 if (!iterator.hasNext() || aggregate) { 12 ClassLoader cl = callingClass.getClassLoader(); 13 14 if (cl != null) { 15 iterator.addEnumeration(cl.getResources(resourceName)); 16 } 17 } 18 19 if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 20 return getResources('/' + resourceName, callingClass, aggregate); 21 } 22 23 return iterator; 24 }
也该到尽头了吧!!从代码中可以看出,struts加载的配置文件都是从类加载器加载.class文件的路径中去寻找的,把配置文件放到WEB-INF下,它当然加载不到了,除非你写成http://xxx/工程名+配置文件路径,否则只能把配置文件写到src下了,这点struts2设计的还真是让人有点郁闷!