Struts2源碼閱讀(三)_Dispatcher&ConfigurationProvider

首先強調一下struts2的線程程安全,在Struts2中大量採用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個線程都有自己獨立的實例變量,互不相干.

 

接下來就從Dispatcher開始看起,先看其構造函數:

 

 
  1. //創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方      
  2.     public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {     
  3.         this.servletContext = servletContext;     
  4.         //配置在web.xml中的param參數      
  5.         this.initParams = initParams;     
  6.     }   

 

我們再看在FilterDispatcher創建Dispatcher的:

 

 
  1. protected Dispatcher createDispatcher(FilterConfig filterConfig) {     
  2.     Map<String, String> params = new HashMap<String, String>();     
  3.     for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {     
  4.         String name = (String) e.nextElement();     
  5.         String value = filterConfig.getInitParameter(name);     
  6.         params.put(name, value);     
  7.     }     
  8. 都可以從FilterConfig中得到     
  9.     return new Dispatcher(filterConfig.getServletContext(), params);     
  10. }  

 

分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.
將各種ConfigurationProvider初始化之後將實例添加到ConfigurationManager的List裏面.
最後通過循環調用List裏的這些destroy(),register()等方法實現對配置文件的屬性進行註冊和銷燬等功能.
下面將分析這七層功夫是怎樣一步步練成的.

 

首先是init_DefaultProperties()

創建Dispatcher之後,來看init()方法
init()方法是用來Load用戶配置文件,資源文件以及默認的配置文件.

主要分七步走,看下面註釋

 

 
  1. public void init() {     
  2.     
  3.     if (configurationManager == null) {     
  4.     //設置ConfigurationManager的defaultFrameworkBeanName.      
  5.     //這裏DEFAULT_BEAN_NAME爲struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等      
  6.         configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);     
  7.     }     
  8.       //讀取properties信息,默認的default.properties,      
  9.     init_DefaultProperties(); // [1]      
  10. //讀取xml配置文件      
  11.       init_TraditionalXmlConfigurations(); // [2]      
  12. //讀取用戶自定義的struts.properties      
  13.       init_LegacyStrutsProperties(); // [3]      
  14. //自定義的configProviders      
  15.       init_CustomConfigurationProviders(); // [5]      
  16. //載入FilterDispatcher傳進來的initParams      
  17.       init_FilterInitParameters() ; // [6]      
  18. //將配置文件中的bean與具體的類映射      
  19.       init_AliasStandardObjects() ; // [7]      
  20.            
  21. //構建一個用於依賴注射的Container對象      
  22. //在這裏面會循環調用上面七個ConfigurationProvider的register方法      
  23. //其中的重點就是DefaultConfiguration的#reload()方法      
  24.       Container container = init_PreloadConfiguration();     
  25.       container.inject(this);     
  26.       init_CheckConfigurationReloading(container);     
  27.       init_CheckWebLogicWorkaround(container);     
  28.     
  29.       if (!dispatcherListeners.isEmpty()) {     
  30.           for (DispatcherListener l : dispatcherListeners) {     
  31.               l.dispatcherInitialized(this);     
  32.           }     
  33.       }     
  34.   }   

 

分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.
將各種ConfigurationProvider初始化之後將實例添加到ConfigurationManager的List裏面.
最後通過循環調用List裏的這些destroy(),register()等方法實現對配置文件的屬性進行註冊和銷燬等功能.
下面將分析這七層功夫是怎樣一步步練成的.

 

首先是init_DefaultProperties()

 

 
  1. private void init_DefaultProperties() {     
  2.     configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());     
  3. }     
  4. 接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了register()方法     
  5. public void register(ContainerBuilder builder, LocatableProperties props)     
  6.         throws ConfigurationException {     
  7.          
  8.     Settings defaultSettings = null;     
  9.     try {     
  10.         defaultSettings = new PropertiesSettings("org/apache/struts2/default");     
  11.     } catch (Exception e) {     
  12.         throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);     
  13.     }     
  14.          
  15.     loadSettings(props, defaultSettings);     
  16. }    

 

 

 

 
  1. //PropertiesSettings構造方法        
  2.     //讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在classpath裏的struts.properties裏覆寫      
  3.     public PropertiesSettings(String name) {     
  4.              
  5.         URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());     
  6.              
  7.         if (settingsUrl == null) {     
  8.             LOG.debug(name + ".properties missing");     
  9.             settings = new LocatableProperties();     
  10.             return;     
  11.         }     
  12.              
  13.         settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));     
  14.     
  15.         // Load settings      
  16.         InputStream in = null;     
  17.         try {     
  18.             in = settingsUrl.openStream();     
  19.             settings.load(in);     
  20.         } catch (IOException e) {     
  21.             throw new StrutsException("Could not load " + name + ".properties:" + e, e);     
  22.         } finally {     
  23.             if(in != null) {     
  24.                 try {     
  25.                     in.close();     
  26.                 } catch(IOException io) {     
  27.                     LOG.warn("Unable to close input stream", io);     
  28.                 }     
  29.             }     
  30.         }     
  31.     }     
  32.          
  33.     //loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得並存放到LocatableProperties props      
  34.     //這個props是register的一個入參.      
  35.     protected void loadSettings(LocatableProperties props, final Settings settings) {     
  36.         // We are calling the impl methods to get around the single instance of Settings that is expected      
  37.         for (Iterator i = settings.listImpl(); i.hasNext(); ) {     
  38.             String name = (String) i.next();     
  39.             props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));     
  40.         }     
  41.     }  

 

 

再來看第二步:init_TraditionalXmlConfigurations() 

 

 
  1. private void init_TraditionalXmlConfigurations() {     
  2.  //首先讀取web.xml中的config初始參數值         
  3.     //如果沒有配置就使用默認的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml",         
  4.     //這兒就可以看出爲什麼默認的配置文件必須取名爲這三個名稱了         
  5.     //如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可       
  6.     String configPaths = initParams.get("config");     
  7.     if (configPaths == null) {     
  8.         configPaths = DEFAULT_CONFIGURATION_PATHS;     
  9.     }     
  10.     String[] files = configPaths.split("//s*[,]//s*");     
  11.     for (String file : files) {     
  12.         if (file.endsWith(".xml")) {     
  13.             if ("xwork.xml".equals(file)) {     
  14.     //XmlConfigurationProvider負責解析xwork.xml      
  15.                 configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));     
  16.             } else {     
  17.     //其它xml都是由StrutsXmlConfigurationProvider來解析      
  18.                 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));     
  19.             }     
  20.         } else {     
  21.             throw new IllegalArgumentException("Invalid configuration file name");     
  22.         }     
  23.     }     
  24. }    

 

 

對於其它配置文件只用StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實現ConfigurationProvider接口。
類XmlConfigurationProvider負責配置文件的讀取和解析,
首先通過init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) 將configFileName配置文件通過SAX解析方式按照DtdMappings解析成Document對象.
然後通過Provider的register()方法加載"bean"和"constant"屬性,再通過loadPackages()加載package及package中的屬性
addAction()方法負責讀取<action>標籤,並將數據保存在ActionConfig中;
addResultTypes()方法負責將<result-type>標籤轉化爲ResultTypeConfig對象;
loadInterceptors()方法負責將<interceptor>標籤轉化爲InterceptorConfi對象;
loadInterceptorStack()方法負責將<interceptor-ref>標籤轉化爲InterceptorStackConfig對象;
loadInterceptorStacks()方法負責將<interceptor-stack>標籤轉化成InterceptorStackConfig對象。
而上面的方法最終會被addPackage()方法調用,addPackage又會被Provider的loadPackages()調用,將所讀取到的數據彙集到PackageConfig對象中。

 

 
  1. protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {     
  2.      PackageConfig.Builder newPackage = buildPackageContext(packageElement);     
  3.     
  4.      if (newPackage.isNeedsRefresh()) {     
  5.          return newPackage.build();     
  6.      }     
  7.      // add result types (and default result) to this package      
  8.      addResultTypes(newPackage, packageElement);     
  9.      // load the interceptors and interceptor stacks for this package      
  10.      loadInterceptors(newPackage, packageElement);     
  11.      // load the default interceptor reference for this package      
  12.      loadDefaultInterceptorRef(newPackage, packageElement);     
  13.      // load the default class ref for this package      
  14.      loadDefaultCla***ef(newPackage, packageElement);     
  15.      // load the global result list for this package      
  16.      loadGlobalResults(newPackage, packageElement);     
  17.      // load the global exception handler list for this package      
  18.      loadGobalExceptionMappings(newPackage, packageElement);     
  19.      // get actions      
  20.      NodeList actionList = packageElement.getElementsByTagName("action");     
  21.      for (int i = 0; i < actionList.getLength(); i++) {     
  22.          Element actionElement = (Element) actionList.item(i);     
  23.          addAction(actionElement, newPackage);     
  24.      }     
  25.      // load the default action reference for this package      
  26.      loadDefaultActionRef(newPackage, packageElement);     
  27.      PackageConfig cfg = newPackage.build();     
  28.      configuration.addPackageConfig(cfg.getName(), cfg);     
  29.      return cfg;     
  30.  }       
  31.     
  32. loadConfigurationFiles解析讀取xml中的內容     
  33.  private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {           
  34.    ...       
  35. //通過DomHelper調用SAX進行解析xml      
  36. doc = DomHelper.parse(in, dtdMappings);     
  37. ...     
  38.    Element rootElement = doc.getDocumentElement();     
  39.    NodeList children = rootElement.getChildNodes();     
  40.    int childSize = children.getLength();     
  41.     
  42.    for (int i = 0; i < childSize; i++) {     
  43.      Node childNode = children.item(i);     
  44.     
  45.      if (childNode instanceof Element) {     
  46.        Element child = (Element) childNode;     
  47.     
  48.        final String nodeName = child.getNodeName();     
  49.     
  50.        if ("include".equals(nodeName)) {     
  51.          String includeFileName = child.getAttribute("file");     
  52.     
  53.       //解析每個action配置是,對於include文件可以使用通配符*來進行配置         
  54.          //如Struts.xml中可配置成<include file="actions_*.xml"/>        
  55.          if (includeFileName.indexOf('*') != -1) {     
  56.            ClassPathFinder wildcardFinder = new ClassPathFinder();     
  57.            wildcardFinder.setPattern(includeFileName);     
  58.            Vector<String> wildcardMatches = wildcardFinder.findMatches();     
  59.            for (String match : wildcardMatches) {     
  60.        //遞歸Load子file中的<include/>      
  61.              docs.addAll(loadConfigurationFiles(match, child));     
  62.            }     
  63.          } else {     
  64.     
  65.            docs.addAll(loadConfigurationFiles(includeFileName, child));     
  66.          }     
  67.        }     
  68.      }     
  69.    }     
  70.    docs.add(doc);     
  71.    loadedFileUrls.add(url.toString());     
  72.    ...     
  73.    return docs;     
  74.  }    

 

 

Come From:  http://qidaoxp.javaeye.com/blog/494444 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章