zoukankan      html  css  js  c++  java
  • Struts2中properties文件参数定义在XML中通过Constant节点定义的原理

           在Struts2中提供了两种类型的框架级配置文件,XML格式和.properties,其中系统提供的

    dafault.properties文件是struts2默认的框架级参数配置,struts.properties是struts2默认的应用级的参数配

    置,包含所有的应用级的参数对框架级的参数的覆盖,XML中配置properties文件的格式如下:

        <constant name="struts.devMode" value="false" />

        本文将从内部的实现原理,来告诉读者,Struts2是如何实现将struts2的XML文件的constant节点中中定义

    的参值读取出来,并覆盖了struts.properties定义的框架级的参数。

     我们首先要从Dispatcher的init()方法来讲:下面是init方法的定义,

     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     public void init() {
     6 
     7         if (configurationManager == null) {
     8             configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
     9         }
    10 
    11         try {
    12             init_DefaultProperties(); // [1]
    13             init_TraditionalXmlConfigurations(); // [2]
    14             init_LegacyStrutsProperties(); // [3]
    15             init_CustomConfigurationProviders(); // [5]
    16             init_FilterInitParameters() ; // [6]
    17             init_AliasStandardObjects() ; // [7]
    18 
    19             Container container = init_PreloadConfiguration();
    20             container.inject(this);
    21             init_CheckConfigurationReloading(container);
    22             init_CheckWebLogicWorkaround(container);
    23 
    24             if (!dispatcherListeners.isEmpty()) {
    25                 for (DispatcherListener l : dispatcherListeners) {
    26                     l.dispatcherInitialized(this);
    27                 }
    28             }
    29         } catch (Exception ex) {
    30             if (LOG.isErrorEnabled())
    31                 LOG.error("Dispatcher initialization failed", ex);
    32             throw new StrutsException(ex);
    33         }
    34     }

    对上述几个步骤初始化的文件进行介绍:

    [1]:初始化stuts2默认的properties文件加载器(default.properties)

    [2]:初始化XML文件文件加载器

    [3]:初始化的properties文件加载器(struts.properties)

    [4]:初始化用户自定义配置加载器

    [5]:初始化由web.xml传入的运行参数

    [6]:初始化默认容器内置对象加载器

         下面我们来查看读取XML配置文件的加载器(XmlConfigurationProvider)的register方法:我只摘取了读

    取constant节点变量值的一段

               else if ("constant".equals(nodeName)) {
                            String name = child.getAttribute("name");
                            String value = child.getAttribute("value");
                            props.setProperty(name, value, childNode);
                       }

    最终将值读取到props中,register方法 register(ContainerBuilder containerBuilder,

    LocatableProperties props),中props变量,是专门存储properties变量的,其中props.setProperty(name,

    value, childNode);将会覆盖,之前定义的框架级别运行参数,下面介绍下读取default.properties和

    struts.properties参数用到的两个Provider,以及它们读取参数的不同之处。default.properties:

    DefaultPropertiesProvider类,它的register方法定义,

     1 public void register(ContainerBuilder builder, LocatableProperties props)
     2             throws ConfigurationException {
     3         
     4         Settings defaultSettings = null;
     5         try {
     6             defaultSettings = new PropertiesSettings("org/apache/struts2/default");
     7         } catch (Exception e) {
     8             throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
     9         }
    10         
    11         loadSettings(props, defaultSettings);
    12     }

    其中defaultSettings = new PropertiesSettings("org/apache/struts2/default");是将配置文件中的值解析

    出来,也就是properties文件的解析器(PropertiesReader),下面是解析器的构造方法;

     1   /**
     2      * Creates a new properties config given the name of a properties file. The name is expected to NOT have
     3      * the ".properties" file extension.  So when <tt>new PropertiesSettings("foo")</tt> is called
     4      * this class will look in the classpath for the <tt>foo.properties</tt> file.
     5      *
     6      * @param name the name of the properties file, excluding the ".properties" extension.
     7      */
     8     public PropertiesSettings(String name) {
     9         
    10         URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());
    11         
    12         if (settingsUrl == null) {
    13             LOG.debug(name + ".properties missing");
    14             settings = new LocatableProperties();
    15             return;
    16         }
    17         
    18         settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));
    19 
    20         // Load settings
    21         InputStream in = null;
    22         try {
    23             in = settingsUrl.openStream();
    24             settings.load(in);
    25         } catch (IOException e) {
    26             throw new StrutsException("Could not load " + name + ".properties:" + e, e);
    27         } finally {
    28             if(in != null) {
    29                 try {
    30                     in.close();
    31                 } catch(IOException io) {
    32                     LOG.warn("Unable to close input stream", io);
    33                 }
    34             }
    35         }
    36     }

    其中 settings.load(in);是读取真正的properties文件;

     1     @Override
     2     public void load(InputStream in) throws IOException {
     3         Reader reader = new InputStreamReader(in);
     4         PropertiesReader pr = new PropertiesReader(reader);
     5         while (pr.nextProperty()) {
     6             String name = pr.getPropertyName();--读取参数名称
     7             String val = pr.getPropertyValue();--读取参数值
     8             int line = pr.getLineNumber();     --读取参数的行
     9             String desc = convertCommentsToString(pr.getCommentLines());
    10                                  --读取参数的注释信息
    11             Location loc = new LocationImpl(desc, location.getURI(), line, 0);
    12             setProperty(name, val, loc);
    13         }
    14     }

     下面来观察DefaultPropertiesProvider类中的loadSettings()方法,该方法是将从配置文件中读取出来的键值映

    射对保存到LocatableProperties这个类中,这个类是贯穿Struts2初始化主线的。下面来具体的分析该方法,该方

    法在register方法中调用:

     1  public void register(ContainerBuilder builder, LocatableProperties props)
     2             throws ConfigurationException {
     3         
     4         Settings defaultSettings = null;
     5         try {
     6             defaultSettings = new PropertiesSettings("org/apache/struts2/default");
     7         } catch (Exception e) {
     8             throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
     9         }
    10         
    11         loadSettings(props, defaultSettings);--将从default.properties文件中读取出来的值放到prop中
    12     }
    
    

            loadSettings(props, defaultSettings)方法,DefaultPropertiesProvider类继承自

    LegacyPropertiesConfigurationProvider类,它调用了父类中的loadSettings方法:

     1  /**
     2      * @param props
     3      * @param settings
     4      */
     5     protected void loadSettings(LocatableProperties props, final Settings settings) {
     6         // We are calling the impl methods to get around the single instance of Settings that is expected
     7         for (Iterator i = settings.listImpl(); i.hasNext(); ) {
     8             String name = (String) i.next();
     9             props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));
    10         }
    11     }

    上面的方法,将从配置文件读取到properties文件中的值全部的放到LocatableProperties类中。其中

    PropertiesSettings类中已经封装了LocatableProperties类,现在只不过是将PropertiesSetting变量中的值

    拿出来放到loadSettings的那个全局的LocatableProperties类(prop)参数。相当与转移了下位置。下面是

    PropertiesSettings构造方法,他包含了LocatableProperties类的一个变量setting

     1  /**
     2      * Creates a new properties config given the name of a properties file. The name is expected to NOT have
     3      * the ".properties" file extension.  So when <tt>new PropertiesSettings("foo")</tt> is called
     4      * this class will look in the classpath for the <tt>foo.properties</tt> file.
     5      *
     6      * @param name the name of the properties file, excluding the ".properties" extension.
     7      */
     8     public PropertiesSettings(String name) {
     9         
    10         URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());
    11         
    12         if (settingsUrl == null) {
    13             LOG.debug(name + ".properties missing");
    14             settings = new LocatableProperties();
    15             return;
    16         }
    17         --实例化LocatableProperties类
    18         settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));
    19 
    20         // Load settings
    21         InputStream in = null;
    22         try {
    23             in = settingsUrl.openStream();
    24             settings.load(in);--读取配置文件中的参数值和名称
    25         } catch (IOException e) {
    26             throw new StrutsException("Could not load " + name + ".properties:" + e, e);
    27         } finally {
    28             if(in != null) {
    29                 try {
    30                     in.close();
    31                 } catch(IOException io) {
    32                     LOG.warn("Unable to close input stream", io);
    33                 }
    34             }
    35         }
    36     }

    settings.load(in);--读取配置文件中的参数值和名称

     1 @Override
     2     public void load(InputStream in) throws IOException {
     3         Reader reader = new InputStreamReader(in);
     4         PropertiesReader pr = new PropertiesReader(reader);--properties文件加载器
     5         while (pr.nextProperty()) {--pr.nextProperty文件
     6             String name = pr.getPropertyName();--名字
     7             String val = pr.getPropertyValue();--值
     8             int line = pr.getLineNumber();--行号
     9             String desc = convertCommentsToString(pr.getCommentLines());--描述信息
    10             --具体位置信息
    11             Location loc = new LocationImpl(desc, location.getURI(), line, 0);
    12             setProperty(name, val, loc);
    13         }
    14     }

    下面观察下方法:setProperty()方法,它将key和他的位置信息存到propLocations中。

    1     public Object setProperty(String key, String value, Object locationObj) {
    2         Object obj = super.setProperty(key, value);
    3         if (location != null) {
    4             Location loc = LocationUtils.getLocation(locationObj);
    5             propLocations.put(key, loc);
    6         }
    7         return obj;
    8     }

    这时候,我们可以返回去看loadSetting方法:

     1   /**
     2      * @param props
     3      * @param settings
     4      */
     5     protected void loadSettings(LocatableProperties props, final Settings settings) {
     6         // We are calling the impl methods to get around the single instance of Settings that is expected
     7         for (Iterator i = settings.listImpl(); i.hasNext(); ) {
     8             String name = (String) i.next();
     9             props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));
    10         }
    11     }

    它用到了上面的三个方法;可以在DefaultSettings类中查看上述三个方法是如何定义的。

    ----------------------------------------------------------------------------------------

    ----------------------------------------------------------------------------------------

    下面介绍下在DefaultPropertiesProvider和LegacyPropertiesConfigurationProvider读取配置文件的

    不同方式,在LegacyPropertiesConfigurationProvider使用了代理模式;下面来进行讲解:

    DefaultPropertiesProvider的register方法:

     1  public void register(ContainerBuilder builder, LocatableProperties props)
     2             throws ConfigurationException {
     3         
     4         Settings defaultSettings = null;
     5         try {
     6             defaultSettings = new PropertiesSettings("org/apache/struts2/default");
    --直接新建了PropertiesSettings类,该构造
    7 } catch (Exception e) { 8 throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e); 9 } 10 11 loadSettings(props, defaultSettings); 12 }

     它直接defaultSettings = new PropertiesSettings("org/apache/struts2/default");从指定的位置读取数据。

    而LegacyPropertiesConfigurationProvider的register的方法:

     1  public void register(ContainerBuilder builder, LocatableProperties props)
     2             throws ConfigurationException {
     3         
     4         final Settings settings = Settings.getInstance();通过Setting.getInstance()方法加载配置文件
     5         
     6         loadSettings(props, settings);
     7         
     8         // Set default locale by lazily resolving the locale property as needed into a Locale object
     9         builder.factory(Locale.class, new Factory() {
    10             private Locale locale;
    11 
    12             public synchronized Object create(Context context) throws Exception {
    13                 if (locale == null) {
    14                     String loc = context.getContainer().getInstance(String.class, StrutsConstants                        .STRUTS_LOCALE);
    15                     if (loc != null) {
    16                         StringTokenizer localeTokens = new StringTokenizer(loc, "_");
    17                         String lang = null;
    18                         String country = null;
    19 
    20                         if (localeTokens.hasMoreTokens()) {
    21                             lang = localeTokens.nextToken();
    22                         }
    23 
    24                         if (localeTokens.hasMoreTokens()) {
    25                             country = localeTokens.nextToken();
    26                         }
    27                         locale = new Locale(lang, country);
    28                     } else {
    29                         LOG.info("No locale define, substituting the default VM locale");
    30                         locale = Locale.getDefault();
    31                     }
    32                 }
    33                 return locale;
    34             }
    35         });
    36     }

    下面来观察Settings.getInstance()方法,

     1  /**
     2      * Provides the Settings object.
     3      * <p>
     4      * This method will substitute the default instance if another instance is not registered.
     5      * 当默认的实例没有被注册,则使用该方法代替,获取默认的实例对象
     6      * @return the Settings object.
     7      */
     8     public static Settings getInstance() {
     9         return (settingsImpl == null) ? getDefaultInstance() : settingsImpl;
    10     }

    下面我们来观察getDefaultInstance()方法

     1  /**
     2      * Creates a default Settings object.
     3      * <p>
     4      * A default implementation may be specified by the <code>struts.configuration</code> setting;
     5      * otherwise, this method instantiates {@link DefaultSettings} as the default implementation.
     6      *
     7      * @return A default Settings object.
     8      */
     9     private static Settings getDefaultInstance() {
    10         if (defaultImpl == null) {
    11             // Create bootstrap implementation
    12          defaultImpl = new DefaultSettings();--新建一个默认的Setting,但是,上面的是propertiesSetting
    13              下面我们观察DefaultSettings是如何初始化的
    14             // Create default implementation
    15             try {
    16                 String className = get(StrutsConstants.STRUTS_CONFIGURATION);
    17 
    18                 if (!className.equals(defaultImpl.getClass().getName())) {
    19                     try {
    20                         // singleton instances shouldn't be built accessing request or session-specific context data
    21                         defaultImpl = (Settings) ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLoader().loadClass(className), null);
    22                     } catch (Exception e) {
    23                         LOG.error("Settings: Could not instantiate the struts.configuration object, substituting the default implementation.", e);
    24                     }
    25                 }
    26             } catch (IllegalArgumentException ex) {
    27                 // ignore
    28             }
    29         }
    30 
    31         return defaultImpl;
    32     }

     DefaultSetting的构造方法:

     1     /**
     2      * Constructs an instance by loading the standard property files, 
     3      * any custom property files (<code>struts.custom.properties</code>), 
     4      * and any custom message resources ().
     5      * <p>
     6      * Since this constructor  combines Settings from multiple resources,
     7      * it utilizes a {@link DelegatingSettings} instance,
     8      * and all API calls are handled by that instance.
     9      */
    10     public DefaultSettings() {
    11 
    12         ArrayList<Settings> list = new ArrayList<Settings>();
    13 
    14         // stuts.properties, default.properties
    15         try {这里用了一个list
    16             list.add(new PropertiesSettings("struts"));--开始读取用户配置的struts.properties
    17         } catch (Exception e) {
    18             log.warn("DefaultSettings: Could not find or error in struts.properties", e);
    19         }
    20 
    21         Settings[] settings = new Settings[list.size()];
    22         delegate = new DelegatingSettings(list.toArray(settings));--实际上是PropertiesSettings
    23 
    24         // struts.custom.properties
    25         try {
    26             StringTokenizer customProperties = new StringTokenizer(delegate.getImpl(StrutsConstants.STRUTS_CUSTOM_PROPERTIES), ",");
    27 
    28             while (customProperties.hasMoreTokens()) {
    29                 String name = customProperties.nextToken();
    30 
    31                 try {
    32                     list.add(new PropertiesSettings(name));--如果配置了struts.custom.properties
    33                 } catch (Exception e) {
    34                     log.error("DefaultSettings: Could not find " + name + ".properties. Skipping.");
    35                 }
    36             }
    37 
    38             settings = new Settings[list.size()];
    39             delegate = new DelegatingSettings(list.toArray(settings));--代理
    40         } catch (IllegalArgumentException e) {
    41             // Assume it's OK, since IllegalArgumentException is thrown  
    42             // when Settings is unable to find a certain setting,
    43             // like the struts.custom.properties, which is commented out
    44         }
    45 
    46     }

    下面来观察代理的两个方法:

    1     // See superclass for Javadoc
    2     public void setImpl(String name, String value) throws IllegalArgumentException, UnsupportedOperationException {
    3         delegate.setImpl(name, value);
    4     }
    5 
    6     // See superclass for Javadoc
    7     public String getImpl(String aName) throws IllegalArgumentException {
    8         return delegate.getImpl(aName);
    9     }

    实际上,上述两个方法,最终会调用:

     1   // See superclass for Javadoc
     2     public void setImpl(String name, String value) throws IllegalArgumentException, UnsupportedOperationException {
     3         IllegalArgumentException e = null;
     4 
     5         for (Settings delegate : delegates) {
     6             try {
     7                 delegate.getImpl(name); // Throws exception if not found
     8                 delegate.setImpl(name, value); // Found it
     9                 return; // Done
    10             } catch (IllegalArgumentException ex) {
    11                 e = ex;
    12 
    13                 // Try next delegate:执行下一个代理的类
    14             }
    15         }
    16 
    17         throw e;--如果最终未找到,则抛出异常,否则在循环中就return了。
    18     }
    19 
    20     // See superclass for Javadoc
    21     public String getImpl(String name) throws IllegalArgumentException {
    22 
    23         IllegalArgumentException e = null;
    24 
    25         for (Settings delegate : delegates) {
    26             try {
    27                 return delegate.getImpl(name);  // Throws exception if not found
    28             } catch (IllegalArgumentException ex) {
    29                 e = ex;
    30 
    31                 // Try next delegate:执行下一个代理的类
    32             }
    33         }
    34 
    35         throw e;--如果,最终未找到,则抛出异常,否则在循环中就return
    36     }

    对应在register方法中引用的loadSettings(props, settings);方法;

     1  /**
     2      * @param props
     3      * @param settings
     4      */
     5     protected void loadSettings(LocatableProperties props, final Settings settings) {
     6         // We are calling the impl methods to get around the single instance of Settings that is expected
     7         for (Iterator i = settings.listImpl(); i.hasNext(); ) {
     8             String name = (String) i.next();
     9             props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));
    10         }
    11     }

    这时候,settings.listImpl()方法,实际上最终调用的是DelegatingSettings的listImpl()方法,这个方法会把

    代理类的list中读取的参数值的放到一个list中,然后统一返回settingList.iterator();

     1  // See superclass for Javadoc
     2     public Iterator listImpl() {
     3         boolean workedAtAll = false;
     4 
     5         Set<Object> settingList = new HashSet<Object>();
     6         UnsupportedOperationException e = null;
     7 
     8         for (Settings delegate : delegates) {
     9             try {
    10                 Iterator list = delegate.listImpl();
    11 
    12                 while (list.hasNext()) {
    13                     settingList.add(list.next());--循环迭代,加入到一个新的list中
    14                 }
    15 
    16                 workedAtAll = true;
    17             } catch (UnsupportedOperationException ex) {
    18                 e = ex;
    19 
    20                 // Try next delegate--循环迭代下一个代理的类
    21             }
    22         }
    23 
    24         if (!workedAtAll) {
    25             throw (e == null) ? new UnsupportedOperationException() : e;
    26         } else {
    27             return settingList.iterator();
    28         }

     --end

    I believe that we are who we choose to be. Nobody‘s going to come and save you, you‘ve got to save yourself. 我相信我们成为怎样的人是我们自己的选择。没有人会来拯救你,你必须要自己拯救自己。
  • 相关阅读:
    Gmail总是把MS发来的信作为垃圾邮件
    添加了CSpreadSheet.h后出现LNK2005错误
    弃用Eudora
    卸载Intel C++ Compiler后Visual C++ 6.0不能工作了
    Matlab对多CPU系统的支持
    Borland决定出售Delphi、JBuilder、C++Builder等IDE产品
    微软提供的免费软件
    生命在于运动?
    "Cannot modify header information"的解决方法
    慢慢的,我们长成了受困于数字的大人
  • 原文地址:https://www.cnblogs.com/caroline/p/2981574.html
Copyright © 2011-2022 走看看