引言:
从去年开始,自己的技术学习就开始转向开源软件和移动开发这两个方向,不过其实一直都进展的不顺利,因为自己的目标太大,想一下子做出个“惊人”的举动。不过自己的技术积累和经验实在太少,踌躇的时间多过了看书学习的时间,实在可惜。
今年我决定以这种在看书的时候碎思的形式先时时记录自己的一些想法,但愿可以从小的、从简单的学起、做起。
一、StringManager是做什么的?
对于Tomcat这种“大型”的软件来说,在运行过程中,必然需要处理大量的错误信息,然后通过这些错误信息来调整Tomcat的配置或是程序等。目前Tomcat的错误信息都是以属性(Porperites)文件保存了所有的错误信息,在最新版本的Tomat7提供了4种语言(英语、法语、西班牙和日语)。
我们可以从Apache网站上下载一份Tomcat源代码,按照如下路径可以看到这些属性文件java/org/apache/catalina/util下面看到名为LocalStrings.properties的文件。
另外还有一种简单的方法是通过开源中国(http://www.oschina.net)网站的查看源代码找到,路径为: http://www.oschina.net/code/explore/tomcat-7.0.5/java/org/apache/catalina/util/LocalStrings.properties
打开这个属性文件,可以看到类似如下内容:
parameterMap.locked=No modifications are allowed to a locked ParameterMap
extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension [{2}] not found.
Properties文件是以键值对的形式保存,同时注意上面第二行{}里的数字,可以根据位置填入指定的参数。
前面也已经提到Tomcat中有很多类需要纪录错误,这里是采取在每个包(Package)内使用一个上面这样的properties文件保存这个包中可能会出现这样的错误,然后在这个包中可以调用StringManager对象以“键”来获取properties文件的“值”,注意这里是在每个包中最多只会有一个StringManager对象,即所谓的单例模式:
二、单例模式(singleton)
顾名思义,就是只有一个实例。
作为对象的创建模式[GOF95], 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
(来自 http://baike.baidu.com/view/1859857.htm)
关于单例模式的使用情况网上有很多,这里就不再摘抄,就事论事来说,我认为这里主要是考虑到Tomcat的各个类在使用StringManager的时候可能会存在并发的情况,如果存在多个StringManager的对象的话,对于资源文件的读取会出现“错误”,因此对应一个包中一个StringManager,这个也是目前的想法,如果有错误或其他的想法,欢迎随时和我讨论。
好,前面已经说了不少“废话”,作为程序员,我们还是来看代码更踏实点:
三、代码解析
下面个类是我从Tomcat源代码中找出,然后只将记入日志的部分删掉,下面就具体的代码结合上面的单例模式谈些我的想法吧。(方法附在最后)
1 从成员变量上看
两个私有变量:
一个是用于读取指定资源的类,至于ResourceBundle类和Properites类的区别,目前我认为主要是在前者是专门用于处理国际化文件,前面我们也提到,目前最新版本的Tomcat是支持4国语言的,所以使用ResourceBundle来读取相应的配置文件。
另一个是静态的Hashtable对象,以包名为Key,以该包的Stringmanger对象为Value,后面取得Stringmager对象的时候都从这里根据包名取得,如果没有再构造一个Stringmanager对象。
2 从构造方法上看
该类仅提供了一个私有的构造方法,即不允许直接构造Stringmanager对象,这也是单例模式的实现的重点之一,取得唯一的对象是需要通过专门的方法。
构造方法是传入一个包名,然后根据包名加上固定的后缀文件名来构造出一个ResourceBundle对象,并赋值给指定的私有变量。
3 从功能方法来看
可以看到,代码中有好多getString的方法,其功能是根据指定的Key从资源文件中取得对应的值。通过多态,主要是为了实现格式化对应的值,即填充资源文件格式中有类似{1}的位置,最终给予用户更有效的报错信息。
这里多说一点,关于面向对象的3个特征,其实都已经背的熟的不能再熟,但如何能够有效且合理的使用,真的很难,个人工作中,认为多态尤其难掌握,在平时阅读一些好的源代码的时候,会对这些使用“拍案叫好”,也再次觉得自己太嫩。
4 从获取方法来看
这里是对应getManager方法,可以看做这个类的“核心”,其他类需要使用StringManager这里的时候都是通过这个方法来获取StringManager对象。
因为会在Tomcat运行当中存在并发调用的能看,因此这个getManager的方法是线程安全的,加入了synchronized来保证,同时前面也提到了必须通过getManager的方法来获取StringManager对象,因此必须是静态方法,能够直接调用。
方法体内,会先从HashTable中根据需要获得资源的包名来找到是否已经构造了该对象,如果有则直接返回,没有则调用私有的构造方法创建,并将这个对象放入HashTable中。
至此,我简单的将这个类进行了一个简单的分析,整篇分析耗时比我想象的要长,而且在内容细致程度和语言上也有所欠缺,如果有错漏,欢迎给我留言并进行讨论。
package org.apache.catalina.util; import java.text.MessageFormat; import java.util.Hashtable; import java.util.MissingResourceException; import java.util.ResourceBundle; public class StringManager { private ResourceBundle bundle; private static Hashtable managers = new Hashtable(); private StringManager(String packageName){ String bundleName = packageName+".LocalStrings"; bundle = ResourceBundle.getBundle(bundleName); } public String getString(String key){ if(key == null){ String msg = "key is null"; throw new NullPointerException(msg); } String str = null; try{ str = bundle.getString(key); }catch(MissingResourceException mre){ str = "Cannot find message associate with key = '"+ key + "'"; } return str; } public String getString(String key,Object[] args){ String isString = null; String value = getString(key); try{ Object nonNullArgs[] = args; for(int i = 0; i<args.length;i++ ){ if(args[i] == null){ if(nonNullArgs == args) nonNullArgs=(Object[])args.clone(); nonNullArgs[i] = "null"; } } isString = MessageFormat.format(value, nonNullArgs); }catch (IllegalArgumentException iae) { StringBuffer buf = new StringBuffer(); buf.append(value); for(int i = 0 ;i<args.length;i++){ buf.append("arg[" + i + "]=" + args[i]); } isString = buf.toString(); } return isString; } public String getString(String key,Object arg){ Object[] args = new Object[]{arg}; return getString(key, args); } public String getString(String key,Object arg1,Object arg2){ Object[] args = new Object[]{arg1,arg2}; return getString(key,args); } public String getString(String key,Object arg1, Object arg2,Object arg3){ Object[] args = new Object[]{arg1,arg2,arg3}; return getString(key,args); } public String getString(String key,Object arg1,Object arg2,Object arg3,Object arg4){ Object[] args = new Object[]{arg1,arg2,arg3,arg4}; return getString(key,args); } public synchronized static StringManager getManager(String packageName){ StringManager mgr = (StringManager)managers.get(packageName); if(mgr == null){ mgr = new StringManager(packageName); managers.put(packageName, mgr); } return mgr; } }