關於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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM