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);
}
}
像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);
}
}
相关文章推荐
- tomcat国际化
- java.lang.UnsatisfiedLinkError: D:\Tomcat-7.0.59\apache-tomcat-7.0.59\bin\tcnative-1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
- Tomcat设置系统启动
- 工作总结19 tomcat下的web.xml和项目中的web.xml
- eclipse中新建Server服务器,使用Tomcat7
- Jenkins部署war包到tomcat7,报错
- web应用开发入门-使用mac版本eclipse搭建tomcat下web应用项目详细步骤
- tomcat jboss 對於CPU及內存佔用過高的分析
- 向服务器中发布war包的命令
- 启动tomcat出错:A child container failed during start
- tomcat报404
- Tomcat热部署
- 有关使用Maven常见问题总结(Eclipse中使用Maven、Maven项目部署到tomcat等问题)
- centos6.5配置tomcat7.0+jdk 1.8
- 怎么解决tomcat占用8080端口问题图文教程
- tomcat 内存配置(转)
- 在tomcat中部署项目
- nginx+tomcat负载均衡策略
- tomcat catalina.home和catalina.base区别
- eclpise热加载,不用重启tomcate