转载

Webwork 学习之路(四)Configuration 详解

Webwork做为经典的Web MVC 框架,个人觉得源码中配置文件这部分代码的实现十分考究,支持自定义自己的配置文件、自定义配置文件读取类、自定义国际化支持。可以作为参考,单独引入到其他项目中,下面是Configuration相关类的继承关系:

Webwork 学习之路(四)Configuration 详解

1. Configuration

  • Configuration 作为 webwork 配置文件的核心类,起到了配置信息读取的门户,默认实现类中间引入了代理类 DelegatingConfiguration 与底层的具体实现读取的 PropertiesConfiguration 完全解耦。在项目中使用时,只需要引入 Configuration 类,如下代码即可获取配置信息;
 Configuration.getString("webwork.locale") 
  • getString 方法会调用 Configuration 自身的get 方法,get 方法中调用 getConfiguration 方法:
 1     public static String getString(String name) throws IllegalArgumentException { 2         String val = get(name).toString(); 3         return val; 4     } 
 1     public static Object get(String name) throws IllegalArgumentException { 2         Object val = getConfiguration().getImpl(name); 3         return val; 4     } 5  6     public static Configuration getConfiguration() { 7         return configurationImpl == null ? getDefaultConfiguration() : configurationImpl; 8     } 
  • Configuration 中定义的两个静态变量defaultImpl 和configurationImpl,还有 一个setConfiguration方法用来设置configurationImpl;
  • defaultImpl 是 WebWork 的默认实现类实例的引用,在每一次读取配置文件时,都会去判断是否在  webwork.properties  是否配置了  webwork.configuration 参数(其实框架是无法实现热读配置文件的,下面会说到,每次判 断只是确定读取配置信息,使用的框架默认类还是用户自定义类);
  • 如果设置了在调用 getDefaultConfiguration() 获得自定义读取类引用 configurationImpl,否则返回 WebWork 自己的 Configuration 实现。

(这里要说一下,随意变动上线的配置文件,你会悲剧的,修改前记得问清楚)

  1     private static Configuration getDefaultConfiguration() {  2         if (defaultImpl == null) {  3             defaultImpl = new DefaultConfiguration();  4             try {  5                 String className = getString("webwork.configuration");  6                 if (!className.equals(defaultImpl.getClass().getName())) {  7                     try {  8                         defaultImpl = (Configuration) ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLoader().loadClass(className));  9                     } catch (Exception e) { 10                         log.error("Could not instantiate configuration", e); 11                     } 12                 } 13                 return defaultImpl; 14             } catch (IllegalArgumentException localIllegalArgumentException) { 15             } 16         } 17         return defaultImpl; 18     } 
  • 第一次调用 getDefaultConfiguration() 方法时,默认实现 defaultImpl 是空,则进入创建一 个 WebWork 自己的实现 DefaultConfiguration 的实例,并通过这个实例读取 WebWork 配置信息。
  • 上面代码第5行有个特殊的地方,和下面的 DelegatingConfiguration中的很相似,下面一起说。

2. DefaultConfiguration

   1 public DefaultConfiguration() {  2         ArrayList list = new ArrayList();  3         try {  4             list.add(new PropertiesConfiguration("webwork"));  5         } catch (Exception e) {  6             this.log.warn("Could not find webwork.properties");  7         }  8         try {  9             list.add(new PropertiesConfiguration("com/opensymphony/webwork/default")); 10         } catch (Exception e) { 11             this.log.error("Could not find com/opensymphony/webwork/default.properties", e); 12         } 13         Configuration[] configList = new Configuration[list.size()]; 14         this.config = new DelegatingConfiguration((Configuration[]) list.toArray(configList)); 15         try { 16             StringTokenizer configFiles = new StringTokenizer((String) this.config.getImpl("webwork.custom.properties"), ","); 17             while (configFiles.hasMoreTokens()) { 18                 String name = configFiles.nextToken(); 19                 try { 20                     list.add(new PropertiesConfiguration(name)); 21                 } catch (Exception e) { 22                     this.log.error("Could not find " + name + ".properties. Skipping"); 23                 } 24             } 25             configList = new Configuration[list.size()]; 26             this.config = new DelegatingConfiguration((Configuration[]) list.toArray(configList)); 27         } catch (IllegalArgumentException localIllegalArgumentException) { 28         } 29         try { 30             StringTokenizer bundleFiles = new StringTokenizer((String) this.config.getImpl("webwork.custom.i18n.resources"), ","); 31             while (bundleFiles.hasMoreTokens()) { 32                 String name = bundleFiles.nextToken(); 33                 try { 34                     this.log.info("Loading global messages from " + name); 35                     LocalizedTextUtil.addDefaultResourceBundle(name); 36                 } catch (Exception e) { 37                     this.log.error("Could not find " + name + ".properties. Skipping"); 38                 } 39             } 40         } catch (IllegalArgumentException localIllegalArgumentException1) { 41         } 42     }  DefaultConfiguration构造函数
  • DefaultConfiguration 并没有直接取读取properties文件,而是通过 PropertiesConfiguration 来实现properties文件的读取;
  • PropertiesConfiguration 也同样是 Configuration 的子类,通过java.util.Properties 来解析properties文件,并 赋予自身Properties 实例settings,并覆盖了父类的setImpl、getImpl、isSetImpl、 listImpl 四个方法;
  • DefaultConfiguration 通 过 PropertiesConfiguration 首 先 加 载 的 是 webwork.properties ,之后又加载了 default.properties;

(这里需要说一下,default.properties 是webwork 框架自身的配置文件,封装在 jar 中,假如你在项目的 web.properties 中定义了与 default.properties 相同的参数,看上面程序配置文件的加载顺序,框架先加载你的配置文件,然后加载默认配置文件,你会发现你的参数是不会起作用的,如果你想让你的参数覆盖框架中的,这里你需要自定义配置文件,并且在 项目 webwork.properties 中配置 webwork.custom.properties  参数)

  • 将 PropertiesConfiguration 实例放入List中,然后创建一个和 List一样大的Configuration[]数组,并把List 转型为 Configuration[]赋予 实例化 DelegatingConfiguration
  • 加载完两个properties 文件,并创建了DelegatingConfiguration 实例之后, DefaultConfiguration开始在这两个属性文件中查找  webwork.custom.properties ,文件名之间用“,”隔开。找到配置后,分割文件名并分别创建 PropertiesConfiguration 实例,加入List,加载完所有配置文 件后重新创建DelegatingConfiguration 实例。
  • 加载完所有的WebWork 属性文件后, 查找属性文件中指定的国际化资源文件(文件名同样用“,”隔开),如果有,则加载到 LocalizedTextUtil 中,供以后使用。

(毕竟是好多年前编写的源码,这里面的 StringTokenizer 出于兼容性的原因已经被遗留(虽然在新代码中并不鼓励使用它)。API 中建议所有寻求此功能的人使用 String 的 split 方法或 java.util.regex 包)

3. DelegatingConfiguration

  • DelegatingConfiguration 也同样是 Configuration 的子类,内部保存了一个 Configuration[]数组configList,并覆 盖了父类的setImpl、getImpl、isSetImpl、listImpl 四个方法,实现对configList 的操作;
  • 如果用户没有指定自己的 Configuration 实现,则 Configuration.getString 最终调用的是 DelegatingConfiguration 的 getImpl;
  • 在 DelegatingConfiguration 的setImpl 方法实现中,有一个特别的地方,其实也就是上面 Configuration提到的f,底层的实现:
  1     public void setImpl(String name, Object value) throws IllegalArgumentException, UnsupportedOperationException {  2         IllegalArgumentException e = null;  3         for (int i = 0; i < this.configList.length; i++) {  4             try {  5                 this.configList[i].getImpl(name);  6   7                 this.configList[i].setImpl(name, value);  8   9                 return; 10             } catch (IllegalArgumentException ex) { 11                 e = ex; 12             } 13         } 14         throw e; 15     } 

WebWork不支持动态的增加属性配置,但允许修改已配置的属性, configList[i].getImpl(name); 调用的是 PropertiesConfiguration 的 getImpl 方法,实现如下:

 1     public Object getImpl(String aName) throws IllegalArgumentException { 2         Object setting = this.settings.get(aName); 3         if (setting == null) { 4             throw new IllegalArgumentException("No such setting:" + aName); 5         } 6         return setting; 7     } 

PropertiesConfiguration 会在settings 里去找name,如果找到就返回配置信息,在 DelegatingConfiguration 的setImpl 方法中通过configList[i].setImpl(name, value)修改该属性的配置,否则抛IllegalArgumentException 异常,该异常在 DelegatingConfiguration 的 getImpl 方法中截 获 ,继续往被调用函数抛。 此时则不会执行 configList[i].setImpl(name, value);从而保证了只有配置过了的属性可以被修改,在服务运行的过程中不会有新增的属性,所有的属性都由 Web 服务第一次启动的时候加载。

4. webwork[三][四]小结

以上分析我们可以看见,Web 服务启动的时候,ServletDispatcher 通过 DefaultConfiguration 先 加 载 webwork.properties 和 default.properties , 并 查 找 webwork.properties中webwork.custom.properties 配置的其他属性文件加载。加载完毕 后再通过属性中配置的 webwork.custom.i18n.resources 加载国际化资源文件供以后 使用。之后再查找 webwork.configuration 属性看是否用户指定了自己的 Configuration 实现,如果有就用用户自己的Configuration 实现,否则返回WebWork 自己的实现(DelegatingConfiguration)。大部分情况下,使用 WebWork 自己的实现 已经足够,用户不需要自己去实现一个 Configuration,除非你想加载XML等格式的配 置文件。

正文到此结束
Loading...