StringManager与单例模式

    在tomcat 源代码中,有这样一个实用类: org.apache.catalina.util.StringManager,基本上每个tomcat组件(如:connector, container, realm, loader, logger等)都会用到它。这是一个管理异常消息的helper class

 

    tomcat这样的Servlet容器,异常消息无论是对系统管理员或者程序员都非常重要。管理员可以通过异常消息,快速定位错误。而对于程序异常,tomcat将异常消息封装到ServletException,来告知程序员Servlet中的错误。

 

    Tomcat如何管理这些异常消息呢?第一个要排除的是硬编码在代码中。这是十分不规范的做法,每一次编辑消息都要重新编译代码,非常麻烦。 Tomcat 将这些消息存入properties文件中,方便编辑。而且利用javaResourceBundle类,可以方便的实现国际化,要知道tomcat是一个使用非常广泛的Servlet容器。然而,tomcat的核心包就有几百个类,如果将这些类要用到的异常消息存入一个properties文件,无疑会带来维护上的噩梦。

 

    Tomcat的做法是一个包共用一个properties文件,如果大家机器上安装有tomcat,可以打开%TOMCAT_HOME%/server/libcatalina.jar(tomcat最核心的包,catalinatomcat的代号)看看,会发现里面基本上每一个包都含有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),以包名作为keyStringManager实例作为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);

	}

}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值