设为首页 收藏本站
查看: 335|回复: 0

[经验分享] Tomcat碎思-StringManager与单例模式

[复制链接]

尚未签到

发表于 2017-1-29 15:13:53 | 显示全部楼层 |阅读模式
  引言:
  从去年开始,自己的技术学习就开始转向开源软件和移动开发这两个方向,不过其实一直都进展的不顺利,因为自己的目标太大,想一下子做出个“惊人”的举动。不过自己的技术积累和经验实在太少,踌躇的时间多过了看书学习的时间,实在可惜。
  今年我决定以这种在看书的时候碎思的形式先时时记录自己的一些想法,但愿可以从小的、从简单的学起、做起。
  一、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 == null){
if(nonNullArgs == args) nonNullArgs=(Object[])args.clone();
nonNullArgs = "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);
}
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;
}
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-335017-1-1.html 上篇帖子: tomcat数据库连接池配置 下篇帖子: 【引用】Tomcat server.xml配置参数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表