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.     }   

//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方 public Dispatcher(ServletContext servletContext, Map<String, String> initParams) { this.servletContext = servletContext; //配置在web.xml中的param参数 this.initParams = initParams; }

 

我们再看在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. }  

protected Dispatcher createDispatcher(FilterConfig filterConfig) { Map<String, String> params = new HashMap<String, String>(); for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) { String name = (String) e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } 都可以从FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); }

 

分七步载入各种配置属性,都是通过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.   }   

public void init() { if (configurationManager == null) { //设置ConfigurationManager的defaultFrameworkBeanName. //这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //读取properties信息,默认的default.properties, init_DefaultProperties(); // [1] //读取xml配置文件 init_TraditionalXmlConfigurations(); // [2] //读取用户自定义的struts.properties init_LegacyStrutsProperties(); // [3] //自定义的configProviders init_CustomConfigurationProviders(); // [5] //载入FilterDispatcher传进来的initParams init_FilterInitParameters() ; // [6] //将配置文件中的bean与具体的类映射 init_AliasStandardObjects() ; // [7] //构建一个用于依赖注射的Container对象 //在这里面会循环调用上面七个ConfigurationProvider的register方法 //其中的重点就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if (!dispatcherListeners.isEmpty()) { for (DispatcherListener l : dispatcherListeners) { l.dispatcherInitialized(this); } } }

 

分七步载入各种配置属性,都是通过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. }    

private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } 接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings("org/apache/struts2/default"); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e); } loadSettings(props, defaultSettings); }  

 

 

 
  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.     }  

//PropertiesSettings构造方法 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写 public PropertiesSettings(String name) { URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass()); if (settingsUrl == null) { LOG.debug(name + ".properties missing"); settings = new LocatableProperties(); return; } settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); // Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch (IOException e) { throw new StrutsException("Could not load " + name + ".properties:" + e, e); } finally { if(in != null) { try { in.close(); } catch(IOException io) { LOG.warn("Unable to close input stream", io); } } } } //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props //这个props是register的一个入参. protected void loadSettings(LocatableProperties props, final Settings settings) { // We are calling the impl methods to get around the single instance of Settings that is expected for (Iterator i = settings.listImpl(); i.hasNext(); ) { String name = (String) i.next(); props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name)); } } 

 

再来看第二步: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. }    

private void init_TraditionalXmlConfigurations() { //首先读取web.xml中的config初始参数值 //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml", //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了 //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可 String configPaths = initParams.get("config"); if (configPaths == null) { configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split("//s*[,]//s*"); for (String file : files) { if (file.endsWith(".xml")) { if ("xwork.xml".equals(file)) { //XmlConfigurationProvider负责解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider来解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException("Invalid configuration file name"); } } }  

 

对于其它配置文件只用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.      loadDefaultClassRef(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.  }    

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException { PackageConfig.Builder newPackage = buildPackageContext(packageElement); if (newPackage.isNeedsRefresh()) { return newPackage.build(); } // add result types (and default result) to this package addResultTypes(newPackage, packageElement); // load the interceptors and interceptor stacks for this package loadInterceptors(newPackage, packageElement); // load the default interceptor reference for this package loadDefaultInterceptorRef(newPackage, packageElement); // load the default class ref for this package loadDefaultClassRef(newPackage, packageElement); // load the global result list for this package loadGlobalResults(newPackage, packageElement); // load the global exception handler list for this package loadGobalExceptionMappings(newPackage, packageElement); // get actions NodeList actionList = packageElement.getElementsByTagName("action"); for (int i = 0; i < actionList.getLength(); i++) { Element actionElement = (Element) actionList.item(i); addAction(actionElement, newPackage); } // load the default action reference for this package loadDefaultActionRef(newPackage, packageElement); PackageConfig cfg = newPackage.build(); configuration.addPackageConfig(cfg.getName(), cfg); return cfg; } loadConfigurationFiles解析读取xml中的内容 private List<Document> loadConfigurationFiles(String fileName, Element includeElement) { ... //通过DomHelper调用SAX进行解析xml doc = DomHelper.parse(in, dtdMappings); ... 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"); //解析每个action配置是,对于include文件可以使用通配符*来进行配置 //如Struts.xml中可配置成<include file="actions_*.xml"/> if (includeFileName.indexOf('*') != -1) { ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Vector<String> wildcardMatches = wildcardFinder.findMatches(); for (String match : wildcardMatches) { //递归Load子file中的<include/> docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); ... return docs; }  

 

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

你可能感兴趣的:(源码,struts2,职场,休闲)