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. 我相信我们成为怎样的人是我们自己的选择。没有人会来拯救你,你必须要自己拯救自己。
原文地址:https://www.cnblogs.com/caroline/p/2981574.html