您的位置:首页 > 运维架构 > Tomcat

Tomcat源码分析之StringManager与单例模式

2016-01-27 11:14 519 查看
在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对象,则会大大的提高效率。怎么做到这一点呢?读到这里,相信熟悉设计模式的读者应该都会想到了吧~ 没错,就是单例模式。

单例

单例模式是对象的创建模式,确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

Java代码



/**

* 单例模式

* @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。

Java代码



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);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: