在tomcat 源代码中,有这样一个实用类: org.apache.catalina.util.StringManager,基本上每个tomcat组件(如:connector, container, realm, loader, logger等)都会用到它。这是一个管理异常消息的helper class。
像tomcat这样的Servlet容器,异常消息无论是对系统管理员或者程序员都非常重要。管理员可以通过异常消息,快速定位错误。而对于程序异常,tomcat将异常消息封装到ServletException,来告知程序员Servlet中的错误。
Tomcat如何管理这些异常消息呢?第一个要排除的是硬编码在代码中。这是十分不规范的做法,每一次编辑消息都要重新编译代码,非常麻烦。 Tomcat 将这些消息存入properties文件中,方便编辑。而且利用java的ResourceBundle类,可以方便的实现国际化,要知道tomcat是一个使用非常广泛的Servlet容器。然而,tomcat的核心包就有几百个类,如果将这些类要用到的异常消息存入一个properties文件,无疑会带来维护上的噩梦。
Tomcat的做法是一个包共用一个properties文件,如果大家机器上安装有tomcat,可以打开%TOMCAT_HOME%/server/lib的catalina.jar(tomcat最核心的包,catalina是tomcat的代号)看看,会发现里面基本上每一个包都含有LocalString.properties文件,如:org.apache.core,就是这些文件,存储了tomcat所要用到的异常消息。
StringManager就是为了处理这些异常消息的helper class, 当包中的某个类需要用到异常消息时,可以先实例化StringManager,然后调用getString(String key)方法
如sm.getString("httpConnector.alreadyInitialized"),
上面的语句返回“HTTP connector has already been initialized”
如果你有LocalStrings_CN.properties文件,则会返回相应的中文消息。
然而,若每个类都实例化一个StringManager对象,而这些对象所包含的异常消息都是相同的,同样也会带来资源上的浪费。若每个包中的类,都共用一个StringManager对象,则会大大的提高效率。怎么做到这一点呢?读到这里,相信熟悉设计模式的读者应该都会想到了吧~ 没错,就是单例模式。
单例
单例模式是对象的创建模式,确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- /**
- * 单例模式
- * @author linhai
- */
- public class Singleton {
- //声明一个静态的实例
- private static Singleton instance;
- //私有化构造函数
- private Singleton(){}
- //静态方法返回自身实例
- public static Singleton getInstance()
- {
- if(instance==null)
- {
- instance=new Singleton();
- }
- return instance;
- }
- }
上面的代码便是单例模式的经典示例。单例的要点有二:一是私有化构造函数,这样其它的类不能进行实行化,保证单例,第二是要提供静态的工厂方法,返回自身实例,让客户端调用:Singleton instance=Singleton.getInstance()。
StringManager
明白了单例模式,要实现一个包只有一个StringManager对象便简单了,可以采用一个Map(tomcat 采用的是HashTable),以包名作为key,StringManager实例作为value。
- package com.scnulh.catalina.util;
- import java.text.MessageFormat;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.MissingResourceException;
- import java.util.ResourceBundle;
- public class StringManager {
- /**
- * ResourceBundle实例,代表存储tomcat异常消息的资源文件
- */
- private ResourceBundle bundle;
- /**
- * 保存StringManager对象的Map,以包名为key,value为StringManager对象
- */
- private static Map<String, StringManager> stringManagers=
- new HashMap<String, StringManager>();
- /**
- * 私有化的构造函数
- * @param packageName 包名
- */
- private StringManager(String packageName)
- {
- //包名加LocalStrings,这也是为什么我们看的资源文件是以LocalStrings命名的原因
- String baseName=packageName+".LocalStrings";
- //根据包名,获取资源文件
- bundle=ResourceBundle.getBundle(baseName);
- }
- /**
- * 返回StringManager实例的静态方法,确保相同的包名返回相同的实例
- * 同步方法
- * @param packageName 包名
- * @return
- */
- public synchronized static StringManager getStringManager(String packageName)
- {
- //先从map中查找
- StringManager stringManager=stringManagers.get(packageName);
- //如果对应包的StringManager未实例化,则实例化,并且放入Map中缓存
- if(stringManager==null)
- {
- stringManager=new StringManager(packageName);
- stringManagers.put(packageName, stringManager);
- }
- return stringManager;
- }
- //============以下为StringManager对象查找异常消息的方法===========
- public String getString(String key)
- {
- //对参数先进行验证
- if(key==null)
- {
- String msg="key is null";
- throw new NullPointerException(msg);
- }
- String result=null;
- try
- {
- result=bundle.getString(key);
- }
- catch(MissingResourceException e)
- {
- result="can not find message with the key "+key;
- }
- return result;
- }
- public String getString(String key,Object[] args)
- {
- String result=null;
- String initMessage=getString(key);
- try
- {
- Object[] notNullArgs=args;
- for(int i=0;i<args.length;i++)
- {
- if(args[i]==null)
- {
- if(notNullArgs==args)
- notNullArgs=(Object[])args.clone();
- args[i]="null";
- }
- }
- //MessageFormat的使用
- result=MessageFormat.format(initMessage, notNullArgs);
- }
- //这里异常的处理值得我们学习
- //估计大部分的程序员都会直接来一句iae.printStackTrace();吧
- catch(IllegalArgumentException iae)
- {
- StringBuilder sb=new StringBuilder();
- sb.append(initMessage);
- for (int i = 0; i < args.length; i++) {
- sb.append(" arg[" + i + "]=" + args[i]);
- }
- result=sb.toString();
- }
- return result;
- }
- //以下是方法的重载,方便客户端的调用
- 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 static void main(String[] args)
- {
- StringManager stringManager=
- StringManager.getStringManager("ex03.pyrmont.connector.http");
- String string=stringManager.getString("httpConnector.alreadyInitialized");
- System.out.println(string);
- string=stringManager.getString("httpConnector.anAddress", "192.165.23.26",12);
- System.out.println(string);
- }
- }