*******************************************************
作者:陈刚,程序员,广西省桂林人,广西师范大学数学系97届毕业。
blog:http://blog.csdn.net/glchengang
Email:glchengang@yeah.net
*******************************************************
对Spring耳闻已久,但一直没有时间和心情去看它,最近它的声音是越来越大了,Java视线http://forum.javaeye.com/有不高手在谈论它。于是趁着有空闲时间,我也花了两个晚上看了看Spring,看的是夏昕的<Spring开发指南>http://www.xiaxin.net/Spring_Dev_Guide.rar,文章写得不错。以下谈谈我的学习感受
一、Spring的IoC(Inversion of Control)。
这是Spring中得有特点的一部份。IoC又被翻译成“控制反转”,也不知道是谁翻译得这么别扭,感觉很深奥的词。其实,原理很简单,用一句通俗的话来说:就是用XML来定义生成的对象。IoC其实是一种设计模式,Spring只是实现了这种设计模式。
这种设计模式是怎么来的呢?是实践中逐渐形成的。
第一阶段:用普通的无模式来写Java程序。一般初学者都要经过这个阶段。
第二阶段:频繁的开始使用接口,这时,接口一般都会伴随着使用工厂模式。
第三阶段:使用IoC模式。工厂模式还不够好:(1)因为的类的生成代码写死在程序里,如果你要换一个子类,就要修改工厂方法。(2)一个接口常常意味着一个生成工厂,会多出很多工厂类。
可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
IoC中最基本的Java技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。我在最近的一个项目也用到了反射,当时是给出一个.properties文本文件,里面写了一些全类名(包名+类名),然后,要根据这些全类名在程序中生成它们的对象。反射的应用是很广泛的,象Hibernate、String中都是用“反射”做为最基本的技术手段。
在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普通应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。
所以要理解IoC,你必须先了解工厂模式和反射编程,否则对它产生的前因后果和实现原理都是无法理解透彻的。只要你理解了这一点,你自己也完全可以自己在程序中实现一个IoC框架,只不是这还要涉及到XML解析等其他知识,稍微麻烦一些。
IoC最大的好处是什么?因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是现实于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(有点象USB接口和SCIS硬盘了)。
IoC最大的缺点是什么?(1)生成一个对象的步骤变复杂了(其实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观。(2)对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。(3)缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了,这似乎是所有XML方式的缺憾所在。
总的来说IoC无论原理和实现都还算是很简单的。一些人曾认为IoC没什么实际作用,这种说法是可以理解的,因为如果你在编程中很少使用接口,或很少使用工厂模式,那么你根本就没有使用IoC的强烈需要,也不会体会到IoC可贵之处。有些人也说要消除工厂模式、单例模式,但是都语焉不详、人云亦云。但如果你看到IoC模式和用上Spring,那么工厂模式和单例模式的确基本上可以不用了。但它消失了吗?没有!Spring的IoC实现本身就是一个大工厂,其中也包含了单例对象生成方式,只要用一个设置就可以让对象生成由普通方式变单一实例方式,非常之简单。
总结:
(1)IoC原理很简单,作用的针对性也很强,不要把它看得很玄乎。
(2)要理解IoC,首先要了解“工厂、接口、反射”这些概念。
二、Spring的MVC
如果你已经熟悉Struts,那么不必把MVC做为重点学习内容。基本上我认为Spring MVC是一个鸡肋,它的技术上很先进,但易用性上没有Struts好。而且Struts有这么多年的基础了,Spring很难取代Struts的地位。这就是先入为主的优秀,一个项目经理选用一种框架,不能单纯的从它的技术上考虑,还有开发效率,人员配置等都是考虑因素。但做为研究性的学习,Spring的MVC部份还是蛮有价值的。
三、数据库层的模板
Spring主要是提供了一些数据库模板(模板也是一种Java设计模式),让数据部分的代码更简洁,那些try...catch都可以不见了。这个的确是个好东东。
四、AOP
AOP又称面向方面编程,它的实现原理还是用了反射:通过对某一个种类的方法名做监控来实现统一处理。比如:监控以“insert”字符串开头的方法名,在这种方法执行的前后进行某种处理(数据库事务等)。但这里我有一个疑问?不一定所有以insert开头的方法都是数据库操作,哪么当某个insert开头的方法不是数据库操作,你又对它进行了数据事务的操作,这样的错误如何防止???我对这方面了解不深,还是只知道一个大概。
曾看过一个程序员发出这样的感慨:“框架一个接一个,学也学不完,而且有必要吗?这样一层层的加上框架,还不如直接写JSP来得直接,效率还高”。我想这种困惑很多人都有吧?但如果你经过的项目渐多,就会发现,维护项目要比开发项目更艰难,代价更大。那种用JSP直接来写,层次又不清楚的开发,往往最后得到一个不可再修改的软件,一团乱麻,移一发而动全身。但软件不象电视机,做好了就不会改动了,软件是一个变化的事物,用户的需求随时会改变,这时你会体会到分层和使用框架的好处了,它们为你做了软件中很多和业务无关的工作,你可以只关注业务,并减少代码量。唯一缺点就是有一个学习的代价,框架配置上也较麻烦。
学习框架,我认为应该:第一步,了解这个框架中的一些关键概念,它的具体含义是什么。第二步,了解这个框架的精华在哪里,它能对开发起到什么样的作用,最好能对它的原理有一定的了解。第三步,用这个框架来写几个例子,实际体会一下。我现在还是刚刚大概完成了前两步,这几天会再看看Spring的文档并用Spring写几个例子,到时一起发出来。
另外,很赞赏<Spring开发指南>的作者夏昕的观点,将自已的经验写成文档公开出来,不过一个人的力量终究太弱。最好能够形成一个组织,对一种新技术,由一两个人出一个大纲,大家把它分了,更写一章,然后由两三个人总集起。我个人感觉,由于英文语言的关系,新技术引进到国内的还是太慢了,至少要比国外慢上一年以上,成立一个开源文档组织还是很有意义的事。
第一章 Spring的下载和安装
下载主页http://www.springframework.org/download.html ,或者直接使用链接地址:http://voxel.dl.sourceforge.net/sourceforge/springframework/spring-framework-1.1.4-with-dependencies.zip
Spring的下载包有两种:spring-framework-1.1.4-with-dependencies.zip和spring-framework-1.1.4.zip,上面的第二个链接就是下载前者,建议你也下载前者,因为前者比后者多了一些Spring要用到的第三方包,如hibernate、j2ee、dom4j、aopalliance、jakarta-commons等。下载包名称的dependencies就是“依赖”的意思。
1、解压后的目录结构如下:
目录说明:
l dist Spring自已的核心库
l docs 有一些文档。
l lib 是一些用到的第三方库。
l mock 仿制品?????????????我也不知道
l samples 一些项目例子
l src Spring的源代码
l test 测试用例
2、新建一个Eclipse项目
(1)项目名myspring
(2)直接单击“下一步”,再单击“完成”
(3)在项目下创建一个lib目录
(4)将Spring的解压缩目录dist和lib都复制到这个lib目录中,然后前者改名成spring,后者先暂时不动吧,以后用到时才管它。
3、将spring库加入到库引用
将spring库加入到库引用中,有如下两种方法。
方法一:单击“添加JAR”把spring的核心包加入。
方法二:上面的“方法一”简单易行,但如果一个项目要引入的包种类很多,那么就显示得较乱。还有一种操作麻烦,但较清晰一些的方法。这种方法是使用Eclipse中的“用户库”的方式,如下图所示:
最后的结果如下图所示,然后单击“确定”
返回上一界面后,再单击“完成”,得到如下图所示的效果
最后,项目里的spring包的引用都在一个目录下,显示层次感强很多。
以后如果要引入myspring/lib/lib目录下的第三方包,也按方法二较好:将第三方包的目录复制到myspring/lib下,再参照方法二,将其加入库引用中即可
4、设置日志包的库引用
jakarta-commons和log4j包主要是做为Spring的运行时输出log(日志)用,如果不设置日志包,那么日志就没法输出到控制台,不利于开发和调试。设置方式如下:
(1)就照上面的方法,放myspring/lib/lib目录下的log4j目录和jakarta-commons目录往上移一层到myspring/lib目录下。最后设置的结果如下图所示,这里我们把log4j移到了others目录,因为log4j就一个JAR包,专门为它象jakarta-commons创建一个目录和用户库太不值了,以后可能还会有这种引用单个包的时候,到时都放到others目录里好了。
(2)日志的库引用完成之后,还要创建一个日志的配置文件:log4j.properties,其文件内容如下:
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%c{1} - %m%n
log4j.properties文件的创建位置在src目录下,如下图所示:
如果没有设置日志设置或设置不对,在使用控制台时会出现下面所示的红字。
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
第二章 Spring中IoC的入门实例
Spring的模块化是很强的,各个功能模块都是独立的,我们可以选择的使用。这一章先从Spring的IoC开始。所谓IoC就是一个用XML来定义生成对象的模式,我们看看如果来使用的。
1、数据模型。
1、如下图所示有三个类,Human(人类)是接口,Chinese(中国人)是一个子类,American(美国人)是另外一个子类。
源代码如下:
package cn.com.chengang.spring;
public interface Human {
void eat();
void walk();
}
package cn.com.chengang.spring;
public class Chinese implements Human {
/* (非 Javadoc)
* @see cn.com.chengang.spring.Human#eat()
*/
public void eat() {
System.out.println("中国人对吃很有一套");
}
/* (非 Javadoc)
* @see cn.com.chengang.spring.Human#walk()
*/
public void walk() {
System.out.println("中国人行如飞");
}
}
package cn.com.chengang.spring;
public class American implements Human {
/* (非 Javadoc)
* @see cn.com.chengang.spring.Human#eat()
*/
public void eat() {
System.out.println("美国人主要以面包为主");
}
/* (非 Javadoc)
* @see cn.com.chengang.spring.Human#walk()
*/
public void walk() {
System.out.println("美国人以车代步,有四肢退化的趋势");
}
}
2、对以上对象采用工厂模式的用法如下
创建一个工厂类Factory,如下。这个工厂类里定义了两个字符串常量,所标识不同的人种。getHuman方法根据传入参数的字串,来判断要生成什么样的人种。
package cn.com.chengang.spring;
public class Factory {
public final static String CHINESE = "Chinese";
public final static String AMERICAN = "American";
public Human getHuman(String ethnic) {
if (ethnic.equals(CHINESE))
return new Chinese();
else if (ethnic.equals(AMERICAN))
return new American();
else
throw new IllegalArgumentException("参数(人种)错误");
}
}
下面是一个测试的程序,使用工厂方法来得到了不同的“人种对象”,并执行相应的方法。
package cn.com.chengang.spring;
public class ClientTest {
public static void main(String[] args) {
Human human = null;
human = new Factory().getHuman(Factory.CHINESE);
human.eat();
human.walk();
human = new Factory().getHuman(Factory.AMERICAN);
human.eat();
human.walk();
}
}
控制台的打印结果如下:
3、采用Spring的IoC的用法如下:
1、在项目根目录下创建一个bean.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>
<bean id="American" class="cn.com.chengang.spring.American"/>
</beans>
bean.xml的位置如下图,注意不要看花眼把它看成是lib目录下的了,它是在myspring目录下的。
2、修改ClientTest程序如下:
package cn.com.chengang.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class ClientTest {
public final static String CHINESE = "Chinese";
public final static String AMERICAN = "American";
public static void main(String[] args) {
// Human human = null;
// human = new Factory().getHuman(Factory.CHINESE);
// human.eat();
// human.walk();
// human = new Factory().getHuman(Factory.AMERICAN);
// human.eat();
// human.walk();
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
Human human = null;
human = (Human) ctx.getBean(CHINESE);
human.eat();
human.walk();
human = (Human) ctx.getBean(AMERICAN);
human.eat();
human.walk();
}
}
从这个程序可以看到,ctx就相当于原来的Factory工厂,原来的Factory就可以删除掉了。然后又把Factory里的两个常量移到了ClientTest类里,整个程序结构基本一样。
再回头看原来的bean.xml文件的这一句
<bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>
id就是ctx.getBean的参数值,一个字符串。class就是一个类(包名+类名)。然后在ClientTest类里获得Chinese对象就是这么一句
human = (Human) ctx.getBean(CHINESE);
因为getBean方法返回的是Object类型,所以前面要加一个类型转换。
4、总结
(1)也许有人说,IoC和工厂模式不是一样的作用吗,用IoC好象还麻烦一点。
举个例子,如果用户需求发生变化,要把Chinese类修改一下。那么前一种工厂模式,就要更改Factory类的方法,并且重新编译布署。而IoC只需要将class属性改变一下,并且由于IoC利用了Java反射机制,这些对象是动态生成的,这时我们就可以热插拨Chinese对象(不必把原程序停止下来重新编译布署)
(2)也许有人说,即然IoC这么好,那么我把系统所有对象都用IoC方式来生成。
注意,IoC的灵活性是有代价的:设置步骤麻烦、生成对象的方式不直观、反射比正常生成对象在效率上慢一点。因此使用IoC要看有没有必要,我认为比较通用的判断方式是:用到工厂模式的地方都可以考虑用IoC模式。
(3)在上面的IoC的方式里,还有一些可以变化的地方。比如,bean.xml不一定要放在项目录下,也可以放在其他地方,比如cn.com.chengang.spring包里。不过在使用时也要变化一下,如下所示:
new FileSystemXmlApplicationContext("src/cn/com/chengang/spring/bean.xml");
另外,bean.xml也可以改成其他名字。这样我们在系统中就可以分门别类的设置不同的bean.xml。
(4)关于IoC的低侵入性。
什么是低侵入性?如果你用过Struts或EJB就会发现,要继承一些接口或类,才能利用它们的框架开发。这样,系统就被绑定在Struts、EJB上了,对系统的可移植性产生不利的影响。如果代码中很少涉及某一个框架的代码,那么这个框架就可以称做是一个低侵入性的框架。
Spring的侵入性很低,Humen.java、Chinese.java等几个类都不必继承什么接口或类。但在ClientTest里还是有一些Spring的影子:FileSystemXmlApplicationContext类和ctx.getBean方式等。
现在,低侵入性似乎也成了判定一个框架的实现技术好坏的标准之一。
(5)关于bean.xml的用法
bean.xml的用法还有很多,其中内容是相当丰富的。假设Chinese类里有一个humenName属性(姓名),那么原的bean.xml修改如下。此后生成Chinese对象时,“陈刚”这个值将自动设置到Chinese类的humenName属性中。而且由于singleton为true这时生成Chinese对象将采用单例模式,系统仅存在一个Chinese对象实例。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="Chinese" class="cn.com.chengang.spring.Chinese" singleton="true">
<property name="humenName">
<value>陈刚</value>
</property>
</bean>
<bean id="American" class="cn.com.chengang.spring.American"/>
</beans>
关于bean.xml的其它用法,不再详细介绍了,大家自己拿Spring的文档一看就明白了。
第三章 IoC中的国际化(CVS版本:V002)
从这一章开始,我将把实例的项目打开一个CVS版本,不知谁能提供一个FTP空间?
3.1 前言
标题准确来说应该是“使用Spring中的IoC功能来实现我们所开发项目系统的国际化”,国际化不是针对IoC的,而是针对你开发的整个系统。
如果你使用过Eclipse的国际化,或者用过Eclipse的“外部化字符串”向导(Eclipse主菜单:源代码->外部化字符串),那么对Spring提供的国际化功能应该是非常容易理解,两者基本一样,或者说各种Java程序的国际化方式都基本一样。
先谈谈Eclipse国际化的两个组成部分:*.properties的资源文件、获取资源文件内容的Message类。
而Spring则和Eclipse的处理类似:资源文件两者是一样的,不同语言的翻译放在不同的资源文件里,连起名规则都一样;Eclipse的Message类要自己写(代码通用,复制以前项目的即可,或用Eclipse的向导生成一个也行),Spring则已经有写好的Message类,我们在IoC的xml文件里注册一下即可使用(也可以实现Spring的MessageSource接口,自己来写一个Message类,代码并不复杂,不过这没什么必要,用Spring提供的就行了)。
无论是Eclipse的Message类,还是Spring的自带的Message类,或是我们自己写一个Message类,都是使用JDK的java.util.ResourceBundle类来实现*.properties文件的读取。
下面用实例来体会一下,先给出本章完成之后的项目结构的截图:
3.2 简单实例
假设我们有如下程序,程序的作用是打印出一个字符串
package cn.com.chengang.spring;
public class MessageTest {
public static void main(String[] args) {
String str = "ChenGang";
System.out.println(str);
}
}
现在,我们要让这个程序能够根据使用者的语言情况输出不同的字符,比如:对英文使用者输出“ChenGang”,对中文使用者输出“陈刚”,对台湾使用输出“陳剛”等等。这个需求的实现方法如下:
1、创建一系列的资源文件
在cn.com.chengang.spring包下创建以下文件:
(1)messages.properties(默认:英文),内容仅一句,如下
“chengang”是键值,Giles是要输出的英文字符串
(2)messages_zh_CN.properties(简体中文)
“\u9648\u521A”是UNICODE码,对应的中文是“陈刚”
(3)messages_ zh_TW.properties(繁体中文)
“\u9673\u525B”对应的中文是“陳剛”
附注:由于中文是要转换成UNICODE码,在编辑和阅读上有诸多不便,如果是用Eclipse做IDE,则有一个编辑资源文件的插件jinto,下载网址是http://www.guh-software.de/,用它打开的资源文件如下图所示,可以看到三个资源在一个界面反映了出来。
如果你不用Eclipse,而是用Editplugs+JDK的方式来编程(现在还有这样的原始人吗?),你也可以用JDK自带的native2ascii.exe程序来将中文字串转成UNICODE码。Ant中还提供了一个相应的任务:<native2ascii encoding="GBK" src="${src}" dest="${build}"/>,其中GBK是一个中国的字符集。
2、修改bean.xml
将Spring自带的org.springframework.context.support.ResourceBundleMessageSource类注册到bean.xml中,这个类的作用是获取资源文件的内容,注册到IoC的bean.xml文件中是为了自动获得此类的对象(Spring做了一些简化编程的处理)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>
<bean id="American" class="cn.com.chengang.spring.American"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>cn.com.chengang.spring.messages</value>
</list>
</property>
</bean>
</beans>
代码说明:
l id="messageSource" 的设置是不变的、必须的。
l ResourceBundleMessageSource是Spring的一个Message类。这里还有一个选择,用ReloadableResourceBundleMessageSource类,此类可以提供不用重启即可重新加载资源文件的特性(前者对资源文件只加载一次)。对于那种有热修改资源文件的需求,后者比较合适,只是后者在效率上有可能有损耗,因为至少要多一些检查资源文件是否改变的代码(这只是我的猜测,我没有仔佃去读这段的源码)。
l “basenames”是不变的、必须的。它是ResourceBundleMessageSource的一个属性,在源代码中的定义是“private String[] basenames;”,可见它是一个字符串数组。
l “cn.com.chengang.spring.messages”是把资源文件的位置传入到basenames属性中。注意:三个资源文件只需要将共同的主名(红色字体)传入:messages.properties、messages_zh_CN.properties、messages_zh_TW.properties。
3、使用。修改MessageTest类,如下
package cn.com.chengang.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MessageTest {
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
String str = ctx.getMessage("chengang", null, null);
System.out.println(str);
}
}
代码说明:
(1)main方法里
l 第一句取得bean.xml文件的配置信息。
l 第二句从资源文件里得到键值chengang对应的字符串。
l 第三句将字符串打印出来,结果是打印的是“陈刚”,说明读取的是messages_zh_CN.properties资源文件。
(2)ctx.getMessage("chengang", null, null);有三个参数:
l 第一个是资源文件的键值;
l 第二个是资源文件字符串的参数,由于本字符串没有参数,所以用一个null(后面给出了一个用到字符串参数的实例);
l 第三个是一个java.util. Locale类型的参数。参数为null,则表示根据使用者的语言环境来选择Locale,因为我用的是中文版的windows,所以在取字符串时它自动选择了messages_zh_CN.properties资源文件。
这其中还有一个控制点在JVM,JVM会根据当前操作系统的语言环境进行相应处理,我们可以通过在JVM启动参数中追加“-Duser.language=zh_TW”来设定当前JVM语言类型,通过JVM级的设定,也可以实现自动切换所使用的资源文件类型。
所以这里面的控制语言的方式有三种:从最低层的操作系统的Locale设定,到更上一层的JVM的Locale设定,再到程序一级的Locale设定。
3.3 资源文件的其他使用方式:
package cn.com.chengang.spring;
import java.util.Locale;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.context.support.ResourceBundleMessageSource;
public class MessageTest {
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
String str = ctx.getMessage("chengang", null, null);
System.out.println(str); //输出“陈刚”
/*
* 使用了messages.properties
*/
str = ctx.getMessage("chengang", null, new Locale(""));
System.out.println(str);//输出“Giles”
/*
* 使用了messages_zh_CN.properties
*/
str = ctx.getMessage("chengang", null, new Locale("zh", "CN"));
System.out.println(str);//输出“陈刚”
/*
* 使用了messages_zh_TW.properties
*/
str = ctx.getMessage("chengang", null, new Locale("zh", "TW"));
System.out.println(str);//输出“陳剛”
/*
* 使用了messages_zh_TW.properties,从这里可见资源文件的起名可以很随意,
* 比如我们建立一个messages_123.properties,在传参数时候就可以这样:
* new Locale("123"),一样也可以取出messages_123.properties中的值
*/
str = ctx.getMessage("chengang", null, new Locale("zh_TW"));
System.out.println(str);//输出“陳剛”
/*
* 当找不到相应的资源文件时,使用了messages_zh_CN.properties
*/
str = ctx.getMessage("chengang", null, new Locale("abcd"));
System.out.println(str);//输出“陈刚”
/**
* 不通过IoC注册,直接使用ResourceBundleMessageSource类的写法。
*/
ResourceBundleMessageSource s = new ResourceBundleMessageSource();
s.setBasename("cn.com.chengang.spring.messages");
str = s.getMessage("chengang", null, null);
System.out.println(str);//输出“陈刚”
}
}
代码说明:
前面说过控制语言的方式有三种:从最低层的操作系统的Locale设定,到更上一层的JVM的Locale设定,再到程序一级的Locale设定。我认为最佳的方法是在程序一级进行控制:定义一个统一的Locale静态变量,然后整个系统中只使用这一个变量,以后就可以通过界面操作设置此Locale变量的值,让用户来选择他所需的软件语言。而且我们也可以将此静态变量设成null值,来自动选择资源文件。
另外,Locale里也定义了一些常量,我们可以直接使用而不必去new一个Locale对象,如:“Locale.ENGLISH”。
3.4 再一个实例
这个实例演示了如何使用多个资源文件,以及如何使用字符串参数
(1)在cn.com.chengang.spring包下再创建一个资源文件messagesOther_zh_CN.properties
chengang.info=\u9648\u521A\uFF0C\u7F51\u540D\uFF1A{0}\uFF0C\u82F1\u6587\u540D\uFF1A{1}\uFF0CBlog\uFF1A{2}
其中UNICODE字符串对应的中文是:“陈刚,网名:{0},英文名:{1},Blog:{2}”,这个字符串一共有三个参数。
(2)修改 bean.xml文件
因为basenames属性是一个数组,当然也就可以接收多个资源文件设定。具体修改如下面的红字部份
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>
<bean id="American" class="cn.com.chengang.spring.American"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>cn.com.chengang.spring.messages</value>
<value>cn.com.chengang.spring.messagesOther</value>
</list>
</property>
</bean>
</beans>
(3)修改MessageTest类,加入几行使用的代码
String[] strArgs = new String[3];
strArgs[0]="混北民工";
strArgs[1]="Giles";
strArgs[2]="http://blog.csdn.net/glchengang";
str = ctx.getMessage("chengang.info", strArgs, null);
System.out.println(str);
打印出来的结果就是:“陈刚,网名:混北民工,英文名:Giles,Blog:http://blog.csdn.net/glchengang”
3.5 国际化的实践建议
l 建议一个包对应一个资源文件。不要整个系统都使用一个资源文件来翻译,这样单个文档的体积就太大了,不利于维护;当然,也不必一个类对应一个资源文件,这样资源文件又太多了。
l 建议资源文件和其翻译类/包在同一目录下。不过,如果是要将软件打成一外JAR包或WAR包,建议把资源文件分离出来,这样可以修改资源文件,而不必再次打包。
l 建议字符串项的键值上加上其所在的类名。比如:上面的chengang和chengang.info最好是取名成MessageTest.chengang和MessageTest.chengang.info。这样查找使用此键值的类会方便很多。
参考文献
l 夏昕的<<Spring开发指南>> http://www.xiaxin.net/Spring_Dev_Guide.rar