关于Struts2.3.16 struts.xml 位置的坑,你肯定掉过~~~

  最近在整一个简单的框架,Spring4.0.6-Mybatis3-Struts2.3.16. 当中出现了一些坑让人费解。

  本来Spring配置都是完美ok的,但是栽在了struts的配置文件上。下面边听虾米,边开写了 --!

 

  背景:Spring4.0.6-Mybatis3-Struts2.3.16 + maven3

  文件结构:

  工程文件目录结构

  

  配置文件目录结构

  

 

  web.xml 中的struts配置

1     <filter>
2         <filter-name>struts2</filter-name>
4         <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
5         <init-param>
6             <param-name>config</param-name>
7             <param-value>struts-default.xml,struts-plugin.xml,/WEB-INF/classes/struts/action/struts.xml</param-value>
8         </init-param>
9     </filter>

  按照spring的Listener的习惯,给配置文件前面加上了路径/WEB-INF/classes/

  跑到chrome里面一输入地址,蹦出个404 action not found ... 

  what happened ? 明明控制台没有错误输出的啊......

  

  辗转反侧,又加上了log4j来输出日志,难道struts2这货要依靠log4j来替自己说话?

  果然,在配置了log4j之后,struts把他的不痛快吐了一地....

  关于log4j的配置,童鞋们可以上百度,上谷歌,很详细的有木有啊...

 

1 2014-08-07 17:06:02 INFO [com.opensymphony.xwork2.config.providers.XmlConfigurationProvider] Parsing configuration file [struts-default.xml]
2 2014-08-07 17:06:02 INFO [com.opensymphony.xwork2.config.providers.XmlConfigurationProvider] Unable to locate configuration files of the name struts-plugin.xml, skipping
3 2014-08-07 17:06:02 INFO [com.opensymphony.xwork2.config.providers.XmlConfigurationProvider] Parsing configuration file [struts-plugin.xml]
4 2014-08-07 17:06:02 INFO [com.opensymphony.xwork2.config.providers.XmlConfigurationProvider] Unable to locate configuration files of the name \WEB-INF\classes\struts/action/struts.xml, skipping
5 2014-08-07 17:06:02 INFO [com.opensymphony.xwork2.config.providers.XmlConfigurationProvider] Parsing configuration file [\WEB-INF\classes\struts/action/struts.xml]

  很明显,没有加载到struts.xml文件,然后把百度和谷歌翻了个遍,大部分答案都是一样的

  1.当自定义struts的配置文件的时候需要加载 struts-default.xml,struts-plugin.xml 这两个

  2.struts2 加载配置文件是基于src目录

  当然上面这两条是正确的. 但是有人说把/WEB-INF/classes/struts.xml 改成 ../struts.xml ,我还真没成功....

  why ?

  

  试了网上的几种方法都不成功都,决定自己看sruts的源码,断点进入struts的内部一看究竟

  (这里介绍一个eclipse的插件--Java Attach Source :jar包源码下载插件 , 直接在eclipse market里搜索安装后,找到你想到下载源码的jar包,

  右键->Attach Java Source 然后看到progress 完成后就可以点开源码了.)

  (当想要研究某个框架的时候,需要用容器启动断点怎么办?最好的办法就是你写一个类继续它,然后配置里写这个类,打上断点就ok了。)

  好直接上struts  org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter的代码

 1 public void init(FilterConfig filterConfig) throws ServletException {
 2         InitOperations init = new InitOperations();
 3         Dispatcher dispatcher = null;
 4         try {
 5             //初始化filterConfig
 6             FilterHostConfig config = new FilterHostConfig(filterConfig);
 7             //加载与log4j关联的日志类
 8             init.initLogging(config);
 9             //初始化配置文件 大头来了
10             dispatcher = init.initDispatcher(config);
11             //设置配置文件中的内容
12             init.initStaticContentLoader(config, dispatcher);
13             //后面这些是对 url 的解析与 action method 的执行
14             prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
15             execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
16             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
17             
18             postInit(dispatcher, filterConfig);
19         } finally {
20             if (dispatcher != null) {
21                 dispatcher.cleanUpAfterInit();
22             }
23             init.cleanup();
24         }
25     }

 

  当启动进入到StrutsPrepareAndExecuteFilter  类时就进入init方法.

  看到这里的  filterConfig , 网上就有一大帮人说,自定义 struts.xml 时, param-name 要写成filterConfig.

  这是个坑,这里的filterConfig 和 param-name 中的值木有半毛钱关系.... 关于why写config后面再细说.

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>filterConfig</param-name> <!-- 这里绝对是错误的,各位小心 -->
6             <param-value>struts-default.xml,struts-plugin.xml,\WEB-INF\classes\struts/action/struts.xml</param-value>
7         </init-param>
8     </filter>

  好,既然我们找到了加载配置文件的大头,那么我们进入到 org.apache.struts2.dispatcher.ng.InitOperations 

  dispatcher = init.initDispatcher(config); 一探天地吧.

1 public Dispatcher initDispatcher( HostConfig filterConfig ) {
2         //创建Dispatcher filter
3         Dispatcher dispatcher = createDispatcher(filterConfig);
4         //类的初始化
5         dispatcher.init();
6         return dispatcher;
7     }

  进入到org.apache.struts2.dispatcher.Dispatcher  dispatcher.init();  ok , 凶手出来鸟~~~

 1     /**
 2      * Load configurations, including both XML and zero-configuration strategies,
 3      * and update optional settings, including whether to reload configurations and resource files.
 4      *注释中有说到加载配置的顺序
 5      * 1.加载xml文件
 6      * 2.加载 类似 struts.properties 文件
 7      * 3.更新操作设置
 8      * 4.配置是否自动reload加载
 9      */
10     public void init() {
11 
12         if (configurationManager == null) {
13             configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
14         }
15 
16         try {
17             //初始化FileManer
18             init_FileManager();
19             //加载struts.properties 文件 一般都在struts.xml文件中配置了
20             init_DefaultProperties(); // [1]
21             //加载struts.xml 文件
22             init_TraditionalXmlConfigurations(); // [2]
23             //解析配置文件、设置变量等等
24             init_LegacyStrutsProperties(); // [3] //加载默认配置文件解析器 PropertiesConfigurationProvider configg provider
25             init_CustomConfigurationProviders(); // [5] 加载用户自定义的 configuration provider 
26             init_FilterInitParameters() ; // [6] //struts.xml 重新加载相关
27             init_AliasStandardObjects() ; // [7] //国际化相关
28         
29             Container container = init_PreloadConfiguration();//   
30             container.inject(this);
31             init_CheckWebLogicWorkaround(container);
32 
33             if (!dispatcherListeners.isEmpty()) {
34                 for (DispatcherListener l : dispatcherListeners) {
35                     l.dispatcherInitialized(this);
36                 }
37             }
38         } catch (Exception ex) {
39             if (LOG.isErrorEnabled())
40                 LOG.error("Dispatcher initialization failed", ex);
41             throw new StrutsException(ex);
42         }
43     }

 

  我们具体到org.apache.struts2.dispatcher.Dispatcher init_TraditionalXmlConfigurations() 看怎么加载struts.xml的

 1 private void init_TraditionalXmlConfigurations() {
 2         //这里就是我们在web.xml中需要些的param-name 的 name -> config 而不是网上所说的 filterConfig 
 3         String configPaths = initParams.get("config");
 4         if (configPaths == null) {
 5             configPaths = DEFAULT_CONFIGURATION_PATHS;
 6         }
 7         // 将我们要加载的struts 相关的 配置文件 如 struts.xml 但不支持struts*.xml
 8         String[] files = configPaths.split("\\s*[,]\\s*");
 9         for (String file : files) {
10             if (file.endsWith(".xml")) {
11                 if ("xwork.xml".equals(file)) {
12                     configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
13                 } else {
14                     //加载 xml 解释器 , 加载 struts.xml 到 configurationManager 中
15                     configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
16                 }
17             } else {
18                 throw new IllegalArgumentException("Invalid configuration file name");
19             }
20         }
21     }

 

关键时刻到来了 

 1 Container container = init_PreloadConfiguration(); // 加载 struts.xml 配置文件  

 

 1  public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
 2         //这里的resourceName 即 struts.xml 
 3         // callingClass 指类加载器的class
 4          AggregateIterator<URL> iterator = new AggregateIterator<URL>();
 5 
 6          iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
 7 
 8          if (!iterator.hasNext() || aggregate) {
 9              iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
10          }
11 
12          if (!iterator.hasNext() || aggregate) {
13              ClassLoader cl = callingClass.getClassLoader();
14 
15              if (cl != null) {
16                  iterator.addEnumeration(cl.getResources(resourceName));
17              }
18          }
19 
20          if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 
21             //获取 struts.xml 所在的根路径
22              return getResources('/' + resourceName, callingClass, aggregate);
23          }
24 
25          return iterator;
26      }
27      
28      
29      
30      package org.apache.struts2.dispatcher.Dispatcher
31      
32      private Container init_PreloadConfiguration() {
33         // 获取 xml 配置
34         Configuration config = configurationManager.getConfiguration();
35         Container container = config.getContainer();
36 
37         boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
38         LocalizedTextUtil.setReloadBundles(reloadi18n);
39 
40         ContainerHolder.store(container);
41 
42         return container;
43     }
44 package com.opensymphony.xwork2.config.ConfigurationManager 45 46 public synchronized Configuration getConfiguration() { 47 if (configuration == null) { 48 setConfiguration(createConfiguration(defaultFrameworkBeanName)); 49 try { 50 // 加载 配置解析器 providers 51 configuration.reloadContainer(getContainerProviders()); 52 } catch (ConfigurationException e) { 53 setConfiguration(null); 54 throw new ConfigurationException("Unable to load configuration.", e); 55 } 56 } else { 57 conditionalReload(); 58 } 59 60 return configuration; 61 } 62 63 package com.opensymphony.xwork2.config.impl.DefaultConfiguration implements Configuration 64 65 public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException { 66 packageContexts.clear(); 67 loadedFileNames.clear(); 68 List<PackageProvider> packageProviders = new ArrayList<PackageProvider>(); 69 70 ContainerProperties props = new ContainerProperties(); 71 ContainerBuilder builder = new ContainerBuilder(); 72 Container bootstrap = createBootstrapContainer(providers); 73 for (final ContainerProvider containerProvider : providers) 74 { 75 //类加载器注入 xml 解析器 76 bootstrap.inject(containerProvider); 77 // 执行各个解析器的init方法 , 有我们前面加载的proerties,xml 的解析器等等 -- 即加载文件操作 78 containerProvider.init(this); 79 // 注册 xml 即 解析操作 80 containerProvider.register(builder, props); 81 }

 

 先执行 XmlConfigurationProvider 中的 init()

 1 com.opensymphony.xwork2.config.providers
 2     public class XmlConfigurationProvider implements ConfigurationProvider {
 3     public void init(Configuration configuration) {
 4         this.configuration = configuration;
 5         this.includedFileNames = configuration.getLoadedFileNames();
 6         //加载 struts.xml 文档
 7         loadDocuments(configFileName);
 8     }
 9     
10     private void loadDocuments(String configFileName) {
11         try {
12             loadedFileUrls.clear();
13             //继续加载文档
14             documents = loadConfigurationFiles(configFileName, null);
15         } catch (ConfigurationException e) {
16             throw e;
17         } catch (Exception e) {
18             throw new ConfigurationException("Error loading configuration file " + configFileName, e);
19         }
20     }
21     
22     private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
23         List<Document> docs = new ArrayList<Document>();
24         List<Document> finalDocs = new ArrayList<Document>();
25         if (!includedFileNames.contains(fileName)) {
26             if (LOG.isDebugEnabled()) {
27                 LOG.debug("Loading action configurations from: " + fileName);
28             }
29 
30             includedFileNames.add(fileName);
31 
32             Iterator<URL> urls = null;
33             InputStream is = null;
34 
35             IOException ioException = null;
36             try {
37                 // 亮点在此
38                 urls = getConfigurationUrls(fileName);
39             } catch (IOException ex) {
40                 ioException = ex;
41             }
42 
43             if (urls == null || !urls.hasNext()) {
44                 if (errorIfMissing) {
45                     throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
46                 } else {
47                     if (LOG.isInfoEnabled()) {
48                     // 没有找到 配置文件的 日志输出在此
49                     LOG.info("Unable to locate configuration files of the name "
50                             + fileName + ", skipping");
51                     }
52                     return docs;
53                 }
54             }
55 
56             URL url = null;
57             while (urls.hasNext()) {
58                 try {
59                     url = urls.next();
60                     //找到配置文件,并存在的 加载文件
61                     is = fileManager.loadFile(url);
62 
63                     InputSource in = new InputSource(is);
64 
65                     in.setSystemId(url.toString());
66 
67                     docs.add(DomHelper.parse(in, dtdMappings));
68                 } catch (XWorkException e) {
69                     if (includeElement != null) {
70                         throw new ConfigurationException("Unable to load " + url, e, includeElement);
71                     } else {
72                         throw new ConfigurationException("Unable to load " + url, e);
73                     }
74                 } catch (Exception e) {
75                     throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);
76                 } finally {
77                     if (is != null) {
78                         try {
79                             is.close();
80                         } catch (IOException e) {
81                             LOG.error("Unable to close input stream", e);
82                         }
83                     }
84                 }
85             }

 

  好,直接来最终的判断struts,xml的文件路径的方法,终于等到你...

 

 1  public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
 2         //这里的resourceName 即 struts.xml 
 3         // callingClass 指类加载器的class
 4          AggregateIterator<URL> iterator = new AggregateIterator<URL>();
 5      // 在这里就获取到了struts.xml 所在的path了
 6          iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
 7      ///E:/Soft/tomcat-6.0.39-x64/apache-tomcat-6.0.39/webapps/TestDemos/WEB-INF/classes/struts/action/struts.xml 
       //原来他是从WEB-INF/classes 开始滴.... 真是愁死我了... 8 if (!iterator.hasNext() || aggregate) { 9 iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName)); 10 } 11 12 if (!iterator.hasNext() || aggregate) { 13 ClassLoader cl = callingClass.getClassLoader(); 14 15 if (cl != null) { 16 iterator.addEnumeration(cl.getResources(resourceName)); 17 } 18 } 19 20 if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 21 22 return getResources('/' + resourceName, callingClass, aggregate); 23 } 24 25 return iterator; 26 }

  ok, struts2 加载配置文件到此结束了,接下来的就是 register 解析 注册 配置文件了,看了近一天的源码终于弄明白了......各种泪奔啊

 下面简单贴下 解析过程的代码了,解析的部分就类型xml的解析,获取关键字,mapping配置文件中的action和result

  1 // 各种做事情都是 这个provider 这里就是适配器模式来适配各种配置文件.xml,.propertis,.dtd等等
  2 // N 个provider 循环执行
  3 // for (final ContainerProvider containerProvider : providers)
  4         {
  5             bootstrap.inject(containerProvider);
  6             containerProvider.init(this);
  7             containerProvider.register(builder, props);
  8         }
  9         
 10 package com.opensymphony.xwork2.config.providers.XmlConfigurationProvider implements ConfigurationProvider
 11 
 12 public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
 13         if (LOG.isInfoEnabled()) {
 14             LOG.info("Parsing configuration file [" + configFileName + "]");
 15         }
 16         Map<String, Node> loadedBeans = new HashMap<String, Node>();
 17         for (Document doc : documents) {
 18             Element rootElement = doc.getDocumentElement();
 19             NodeList children = rootElement.getChildNodes();
 20             int childSize = children.getLength();
 21 
 22             for (int i = 0; i < childSize; i++) {
 23                 Node childNode = children.item(i);
 24 
 25                 if (childNode instanceof Element) {
 26                     Element child = (Element) childNode;
 27 
 28                     final String nodeName = child.getNodeName();
 29                     // 加载spring中的bean
 30                     if ("bean".equals(nodeName)) {
 31                         String type = child.getAttribute("type");
 32                         String name = child.getAttribute("name");
 33                         String impl = child.getAttribute("class");
 34                         String onlyStatic = child.getAttribute("static");
 35                         String scopeStr = child.getAttribute("scope");
 36                         boolean optional = "true".equals(child.getAttribute("optional"));
 37                         Scope scope = Scope.SINGLETON;
 38                         if ("default".equals(scopeStr)) {
 39                             scope = Scope.DEFAULT;
 40                         } else if ("request".equals(scopeStr)) {
 41                             scope = Scope.REQUEST;
 42                         } else if ("session".equals(scopeStr)) {
 43                             scope = Scope.SESSION;
 44                         } else if ("singleton".equals(scopeStr)) {
 45                             scope = Scope.SINGLETON;
 46                         } else if ("thread".equals(scopeStr)) {
 47                             scope = Scope.THREAD;
 48                         }
 49 
 50                         if (StringUtils.isEmpty(name)) {
 51                             name = Container.DEFAULT_NAME;
 52                         }
 53 
 54                         try {
 55                             Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
 56                             Class ctype = cimpl;
 57                             if (StringUtils.isNotEmpty(type)) {
 58                                 ctype = ClassLoaderUtil.loadClass(type, getClass());
 59                             }
 60                             if ("true".equals(onlyStatic)) {
 61                                 // Force loading of class to detect no class def found exceptions
 62                                 cimpl.getDeclaredClasses();
 63                                 containerBuilder.injectStatics(cimpl);
 64                             } else {
 65                                 if (containerBuilder.contains(ctype, name)) {
 66                                     Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
 67                                     if (throwExceptionOnDuplicateBeans) {
 68                                         throw new ConfigurationException("Bean type " + ctype + " with the name " +
 69                                                 name + " has already been loaded by " + loc, child);
 70                                     }
 71                                 }
 72 
 73                                 // Force loading of class to detect no class def found exceptions
 74                                 cimpl.getDeclaredConstructors();
 75 
 76                                 if (LOG.isDebugEnabled()) {
 77                                     LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
 78                                 }
 79                                 containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
 80                             }
 81                             loadedBeans.put(ctype.getName() + name, child);
 82                         } catch (Throwable ex) {
 83                             if (!optional) {
 84                                 throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
 85                             } else {
 86                                 if (LOG.isDebugEnabled()) {
 87                                     LOG.debug("Unable to load optional class: #0", impl);
 88                                 }
 89                             }
 90                         }
 91                         //解析struts.xml 中的action
 92                     } else if ("constant".equals(nodeName)) {
 93                         String name = child.getAttribute("name");
 94                         String value = child.getAttribute("value");
 95                         props.setProperty(name, value, childNode);
 96                     } else if (nodeName.equals("unknown-handler-stack")) {
 97                         List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>();
 98                         NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref");
 99                         int unknownHandlersSize = unknownHandlers.getLength();
100 
101                         for (int k = 0; k < unknownHandlersSize; k++) {
102                             Element unknownHandler = (Element) unknownHandlers.item(k);
103                             unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name")));
104                         }
105 
106                         if (!unknownHandlerStack.isEmpty())
107                             configuration.setUnknownHandlerStack(unknownHandlerStack);
108                     }
109                 }
110             }
111         }
112     }

 

ok,现在struts 加载完了配置文件、就要进行后面的工作了,后面的内容如果有机会,将在下一篇文章中发出了

这篇有点小长,但是总体还是比较精细,欢迎各位来点评、拍砖.

 

http://i.cnblogs.com/EditPosts.aspx?postid=3897813

 

posted @ 2014-08-08 11:57  角蛙  阅读(4467)  评论(2编辑  收藏  举报