简介: 本文旨在总结灵活、高效的方法,将国际化相关的共性操作抽取出来,对其加以
ORM 配置封装和国际化过滤等功能,在业务逻辑层和数据访问层实现动态元素国际化框架,生成一种基于 J2EE
架构的 Web 应用动态数据国际化框架。该方案已成功应用,可以实现规范、高效的国际化软件开发,减少软件开发所需要的时间和精力。
互联网的发展推动了全世界的交流,需要开发出满足不同地区语言、文化、生活习惯要求的
Web 应用,因此,软件的国际化已成为必须要解决的问题。国内外目前采用的国际化方法存在以下一些不足:
- 已存在的动态数据国际化解决方法不易于移植和复用。
- 没有现成的动态数据国际化解决方案或框架。
针对以上问题,需要提出一个动态数据国际化的解决方案。
为了在短时间内,规范高效的构建出国际化的 Web 应用,需要设计一种易于理解和维护的国际化开发框架。作者旨在将国际化相关的共性操作抽取出来进行一致性处理,生成一种基于
Spring 轻量级框架的、J2EE 架构的 Web 应用国际化框架,简化系统国际化的实现过程。
目标是:
- 使该国际化框架适用于关系数据库或对象关系数据库。
- 将国际化相关的共性操作提取到方面(Aspect)中,使编程人员可以专注于核心业务逻辑的编写,不需要考虑特定国家\语言环境,简化开发。
- 新的框架要易于配置。
- 框架的代码可以复用,能有效地提高系统开发效率。
动态数据国际化框架模型的设计与实现
总体设计
本框架基于 J2EE 三层架构(UI 层、逻辑层和持久层),如图 1 所示。所有层次都应遵循
Unicode 准则,即内部编码采用 Unicode 标准,B/S 架构推荐使用 UTF-8 编码。
图 1. 基于 B/S 的国际化总体逻辑框架
图 2. 动态数据国际化框架设计
该容器由国际化配置的注解、DAO 层资源处理器和充当过滤器角色、实现国际化公共增强功能的方面这
3 部分组合而成。
该容器中,国际化 Aspect,主要根据实体 bean 的注释标签判断访问哪种资源,采用哪种资源处理器进行处理。
该容器中,DAO 层的资源处理器,主要根据实体 Bean 注释中的配置信息,对资源进行存取访问操作,获取资源。
分层结构是基于 B/S 架构的 Java EE 应用程序的标准模式,本框架通过对应用程序划分层次,可以获得各层清晰的功能和职责,简化代码的实现难度。
该框架有以下几点优势:
- 不依靠资源的持久化方式; 可使用资源文件或关系数据库存储信息。用关系数据库存储时,无论是单表还是分表,都可以完成数据存取。
- 国际化功能与业务逻辑解耦;
编程人员可以专注于核心的 BO 层的业务逻辑的编写,把国际化相关的功能提取到方面中。不需要考虑国际化的特定国家
\ 语言,只需编写源代码进行数据库操作、对数据库进行访问即可,提高了对数据库进行访问的安全性。
- 坚持以 OOP 设计为主,AOP 设计为辅,可缩减代码,控制开发成本;
- 用注解实现对象关系数据库间的一一映射。
它管理 Java 类到数据库表的映射,在 JavaBean 中只嵌入注解标签即可,与国际化有关的代码完全提取到注解的实现
Bean 中,JavaBean 中完全不使用国际化相关代码,方便复用和调试?
模型实现
框架的运行时序图如图 3 所示:
图 3. 动态元素国际化方面序列图
分析上面的运行时序可知
- 首先由业务逻辑层对象调用获取实体 Bean 属性值的方法。
- 如果该方法有国际化标签,需要国际化支持,则织入 I18NAspect 方面,进行国际化功能增强。
- 根据属性的注解配置,由相应的处理器进行处理。
- 最后把取得的资源值返回给实体类的属性。
至此完成国际化功能的织入,然后 BO 层可以继续执行核心业务逻辑。下面就逐个组件实现以上功能。
Annotation 注解提供国际化配置
本框架中的 Annotation 用于对类或属性提供国际化的配置信息。共定义
5 个注解:ConnectorType、Connector、Config、Text 和 I18n。它们之间的关系类图如图
4 所示:
图 4. Annotation 类图
I18N 资源连接类型。此标签与 @Connector 联合使用。
功能:用于标识 I18N 资源连接的类型,判断从资源文件中读取静态数据,还是从数据库中读取动态信息。
RelationDatabase:标识从数据库中读取动态国际化信息数据。
PropertiesFile:标识从资源文件中读取静态国际化数据。
连接器,此标签与 @Text 联合使用。
功能:负责连接关系数据库,或是资源文件。
Type:连接类型,决定 I18N 资源处理方法,由 ConnectorType
定义,只可能有读取资源文件或访问数据库两种方式。
Name:连接名称,当访问资源文件时,name 表示资源文件的名称。当访问数据库时,name
表示连接数据的数据源名称,可以根据 name 从 Spring 的配置文件中依赖 IoC 获得数据源连接的相关配置。
类属性的国际化配置项。此标签与 @Text 联合使用。
功能:对象和关系数据库表的映射工具,实现 Java 对象模型和数据库关系模型的互相转化和一一映射。
它包含 4 个属性。标签中 locale 是必选项,指出了所支持的对国家
/ 语言环境。
table,column,keyValueField 项是对关系数据库的支持属性。分别表示该属性对应的数据库表名,字段名和唯一标识该属性的关键字段。
作用在类的属性上,对属性进行国际化配置。
功能:建立不同语言环境下对象和数据库 OR 映射,或建立对象与资源文件中记录的对应关系。
Key:主键,表示唯一记录,若数据库访问则表示确定该属性的唯一关键字段,若读取资源文件则表示该属性对应的键值。
Connector:连接器属性,表示连接关系数据库,还是资源文件。
Configs:配置项数组,每一条配置项对应一种语言的国际化支持。Configs[0]
为英文配置信息,Configs[1] 为中文配置信息。
作用在类上,声明该类需要国际化框架的支持,需要织入国际化方面进行功能增强。唯一属性
defaultLocale 定义了默认的语言环境,初始值是”en_US”。
下面将使用已定义好的 Annotation 注解,用标签修饰类文件。
1. 以访问关系数据库为例,建立国家对象的国家名称属性 name 与数据库字段之间的
OR 映射。类 CountryBean 的属性及方法如图所示
图 5.CountryBean 类图
用标签来对“国家名称”name 属性进行配置,标签声明如下:
清单 1. 使用标签配置属性实例一
@Text( connector=@Connector(name="ds",type=ConnectorType.RelationDatabase),key="id", configs={ @Config(locale="en_US",table="country",column="country_en",keyValueField="id"), @Config(locale="zh_CN",table="country",column="country_ch",keyValueField="id")} )
|
连接器属性 @Connector 表示以访问数据库的方式读取属性值,数据源的名称是"ds"。key="id"表示该数据库表的关键字段是
id 字段,它标识该表内的唯一记录。
国际化配置项 @Config 表示 name 属性与数据库的映射关系。配置项数组分别定义了英文、中文环境下该国家名称属性对应的表,字段,和决定该属性值的关键字段。以英文环境为例,要得到
name 值,需要查询 country 库表的 country_en 字段,并由 id 字段确定是哪一条记录。其中
locale="en_US"表示它为英文环境的配置信息,映射 country 表的
country_en 字段,关键字段为 id。
2. 以资源文件为例,建立国家对象的国家名称属性 name 与资源文件中英文国家名称之间的映射。
清单 2. 使用标签配置属性实例二
@Text(connector=@Connector(name="myresource",type=ConnectorType.PropertiesFile), key="country_name", configs={@Config(locale="en_US"),@Config(locale="zh_CN")} )
|
连接器属性 connector 的配置表示资源为名为 myresource
的资源文件,key 表示关键字是 country_name,国际化配置项 Config 的配置分别表示中英文资源文件的后缀。
国际化资源处理器的设计和实现
如图 5 所示,是处理器接口,及两个处理器实现类之间的关系类图。
图 6. 国际化处理器类图
设计出的国际化处理器由一个接口,两个实现类组成。处理器接口 Handler
用于抽象出资源访问的公共方法,包括初始化方法、处理方法。下面对两种处理器的功能进行描述。
资源文件国际化支持处理器
功能:根据关键字 key,从资源文件 source 中获取静态信息的属性值。其类图如图
7 所示。
图 7. 资源文件国际化支持处理器类图
initial() 初始化方法的设计和实现
目的:用于初始化资源文件国际化支持处理器,设置资源文件名 resource
和关键字 key 的值。资源文件国际化支持处理器初始化功能状态图如图 8 所示。
图 8. 资源文件国际化支持处理器初始化功能状态图
关键代码实现如下所示:
清单 3. 资源文件国际化支持处理器 initial()
方法关键代码
String rsName = text.connector().name();// 由配置标签 text 中读取资源文件名称 // 获取资源文件 Resource resource = context.getResource( "classpath:"+rsName+ "_"+ config.locale()+ ".properties"); setResource(resource); // 设置资源文件 setKey(text.key());// 读取配置标签,设置关键字 key 值
|
handle() 处理方法的设计和实现
资源文件国际化支持处理器 handle() 方法功能流程如图 9 所示。
图 9. 资源文件国际化支持处理器 handle()
方法功能状态图
关键代码如下所示:
清单 4. 资源文件国际化支持处理器 handle()
方法关键代码
Properties properties = new Properties();// 加载资源文件 properties.load(resource.getInputStream()); Object retVal = properties.getProperty(key); // 根据关键字取得属性值
|
关系数据库国际化支持处理器
功能:从关系数据库中存取动态信息。获取对象属性标签中的 OR 映射配置信息,在关系数据库中查询该属性。其类图如图
10 所示。
图 10. 关系数据库国际化支持处理器类图
属性:该处理器有 5 个属性,tableName 为数据库表名称,columnName
为与属性一一映射的字段名称,keyName 为该表的关键字段,keyValue 为确定唯一记录的关键字段的值。以上
4 个属性,为访问数据库提供支持,通过它们拼装完整的 SQL 语句。DataSource 是数据源名称。
initial() 初始化方法的设计和实现
目的:用于初始化关系数据库国际化支持处理器,设置与访问数据库相关的 5
个属性的初值。其中 tableName、columnName 由参数 config 标签中的 table、column
配置信息获取。keyName 由参数 text 标签的属性 key 获取。DataSource 由参数
text 的连接器属性 Connector 的 name 中的配置信息获取。
关键代码实现如下所示:
清单 5. 关系数据库国际化支持处理器 initial()
方法关键代码
String dsName = text.connector().name();// 由 text 标签获得数据源名称 DataSource dataSource = (DataSource) context.getBean(dsName); setDataSource(dataSource); // 设置数据源 setColumnName(config.column());// 设置属性对应字段名称 setTableName(config.table());// 设置要访问的数据库表名称 setKeyName(text.key());// 设置关键字段名称
|
handle() 处理方法的设计和实现
该方法比较简单,根据已有的数据库访问信息访问数据库即可,可以使用利用 JDBC
API,也可 Hibernate 等其它 DAO 框架。
国际化方面的设计与实现
国际化方面要实现封装国际化功能,使其与核心业务逻辑分离解耦。
AOP 将系统关注点封装在“方面”中,在编译期间织入有关“方面”的代码。编写一个方面类
I18NAspect 实现国际化增强。
如图 11 所示,方面 I18NAspect 是运行时的横向动作,可以实现国际化相关功能和核心业务逻辑的分离解耦。
图 11. 对象、joinpoint、方面、数据对象耦合结构图
可以把 Aspect 简单地理解为一个 Advice 增强和一个 Pointcut
切入点的组合,如图 11 所示,每个 Pointcut 实现都会对应有一个的 Advice 实现。Advice
用于封装需要增强的国际化功能;Ponintcut 用于定义何时,何处将 Advice 织入到目标对象,只对符合条件方法的增强。
图 12. Aspect 运行机制流程图
Spring 2.0 引入了 AspectJ 的注解,可以通过 Java
5 注解以 AspectJ 的语法在 Spring 中配置 AOP
AspectJ 是目前最完善的 AOP 解决方案,它扩展了 Java 语法,定义了额外的
AOP 语法关键字,它提供了 AOP 技术的所有特性,包括针对字段的拦截。使用 AspectJ 注解实现
AOP 最大的优点是配置极为简单,可以大大简化代码的编写和 XML 配置文件。用 AspectJ 注解实现
AOP,方面定义为:
@Aspect
public class I18NAspect implements ApplicationContextAware{
… }
该类被标记为 @Aspect,表示这是一个方面类。
下面,将对 I18NAspect 进行详细设计,为其添加 Advice
和 Pointcut。
声明的 i18nAspect 方法有个 ProceedingJoinPoint
参数,通过它可以获得方法调用的所有信息,包括方法参数、目标对象等,然后,手动调用 jointPoint
的 proceed() 方法来完成对目标对象的方法调用。需要进行增强的国际化功能的实现流程如图 13 所示:
图 13. I18N 方面 Advice
功能流程图
Advice 的关键代码如下所示:
清单 6. 关系数据库国际化支持处理器 initial()
方法关键代码
Class c = jointPoint.getTarget().getClass();// 获取目标类 Object obj = jointPoint.getTarget();// 获取目标对象 Object retVal=null; // 声明对象属性的返回值 // 当前处理类是否需要 I18N 支持 if(c.isAnnotationPresent(I18N.class)){ // 取得全部字段,筛选出标记了 Text 的字段 Field[] fields = c.getDeclaredFields(); // 设置环境语言变量 String defaultLocale =( (I18N)c.getAnnotation(I18N.class)).defaultLocale(); String progSetLocale = LocaleContextHolder.getLocale().toString(); if(progSetLocale==null) progSetLocale = defaultLocale; Config usingConfig = null; for(Field f : fields){ if(f.isAnnotationPresent(Text.class)){ // 如果存在 Text 标记,则进行国际化处理 Text text = f.getAnnotation(Text.class); Object value = f.getName(); // 取得所有配置信息,并确定使用的配置信息 Config[] configs = text.configs(); for(Config config : configs){ String configLocale = config.locale(); if(configLocale.equals(progSetLocale)){ usingConfig = config; break; } } if(usingConfig==null){ boolean flag = false; String t = text.connector().type();// 处理映射 if(t.equals(ConnectorType.RelationDatabase)){ // 应用已注入的关系数据库处理器处理映射 if(flag==true) value = rdbHandler.handle(); }else if(t.equals(ConnectorType.PropertiesFile)){ // 处理资源文件映射 if(flag==true) value = propHandler.handle(); } f.setAccessible(true);// 开启设置字段值 f.set(obj, value); // 设置字段值 f.setAccessible(false);// 关闭设置字段值 } } retVal = jointPoint.proceed(); } else { retVal = jointPoint.proceed(); } return retVal;
|
定义 Aspect 的 pointcut
Ponintcut 用于针对特定方法使用方面增强。在调用目标对象的某一业务方法时,能够拦截该方法的调用,可以将方面植入到应用程序的流程中。
execution 匹配方法执行时的切入点,表达式如下:
execution( 修饰符?返回类型 声明类型?方法名称 ( 参数类型
) 异常类型? )
例如,为所有类的 get 方法添加一个 i18nAdvice
@Around("execution(* get*())") public Object i18nAdvice(ProceedingJoinPoint jointPoint) { …… }
|
在执行所有类对象的 get 方法之前,系统关注点 i18nAdvice
将完成国际化公共功能,达到织入国际化管理逻辑的目的。
动态数据国际化框架的应用
本文研究的框架,已经完整应用于一个基于 J2EE 的的 Web 应用系统。系统的中、英文运行界面如图
14、15 所示。
图 14. Web 应用中文运行效果
图 15. Web 应用英文运行效果
图 16. Web 应用气象信息模块的中文运行效果
图 17. Web 应用气象信息模块的英文运行效果
结论
本文旨在生成一种基于 J2EE 架构的 Web 应用动态数据国际化框架。
本框架通过以下方法实现了创新:
- 使用 Annotation 注解提供国际化配置,完成对不同语言环境下、不同持久化方式下的地区 /
语言信息的封装。
- 针对不同的持久化方式,分别实现了“关系数据库国际化支持处理器”和“资源文件国际化处理器”,实现动态数据的存取。
- 借助 Spring 的 AOP 技术,设计并实现了封装国际化功能的 Aspect 方面,将国际化相关的操作提取到方面中,使其与核心业务逻辑分离。
该方案已成功应用于“运动会综合信息查询系统”中。通过使用本框架使用,可以实现高效的国际化软件开发,减少本地化过程所需要的时间和精力,降低软件维护成本。
参考资料
- Spring2.0 宝典:介绍了 Spring 的核心机制、依赖注入、资源访问、AOP 框架、DAO
支持、MVC 框架。
- Struts 应用程序中文显示方案及其国际化技术:介绍并指导使用 StrutsFramework
资源文件来解决网页显示和数据库操作中出现的中文问题。
- Annotation 高级应用:介绍 java annotation 的原理和使用,实例化解释
annotation 和 annotation processing tool(APT)的应用方法。
- Sun.JDK5.0 Documentation [EB/OL].:介绍 JavaTM 2 Platform
Standard Edition 5.0 Development Kit (JDK 5.0) 语法及应用方法。
- developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。
|