求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
基于 AOP 的动态数据国际化框架的设计与实现
 

作者: 齐白钰 ,发布于2013-4-10

 

简介: 本文旨在总结灵活、高效的方法,将国际化相关的共性操作抽取出来,对其加以 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 类图

  • @ConnectorType

I18N 资源连接类型。此标签与 @Connector 联合使用。

功能:用于标识 I18N 资源连接的类型,判断从资源文件中读取静态数据,还是从数据库中读取动态信息。

RelationDatabase:标识从数据库中读取动态国际化信息数据。

PropertiesFile:标识从资源文件中读取静态国际化数据。

  • @Connector

连接器,此标签与 @Text 联合使用。

功能:负责连接关系数据库,或是资源文件。

Type:连接类型,决定 I18N 资源处理方法,由 ConnectorType 定义,只可能有读取资源文件或访问数据库两种方式。

Name:连接名称,当访问资源文件时,name 表示资源文件的名称。当访问数据库时,name 表示连接数据的数据源名称,可以根据 name 从 Spring 的配置文件中依赖 IoC 获得数据源连接的相关配置。

  • @Config

类属性的国际化配置项。此标签与 @Text 联合使用。

功能:对象和关系数据库表的映射工具,实现 Java 对象模型和数据库关系模型的互相转化和一一映射。

它包含 4 个属性。标签中 locale 是必选项,指出了所支持的对国家 / 语言环境。

table,column,keyValueField 项是对关系数据库的支持属性。分别表示该属性对应的数据库表名,字段名和唯一标识该属性的关键字段。

  • @Text

作用在类的属性上,对属性进行国际化配置。

功能:建立不同语言环境下对象和数据库 OR 映射,或建立对象与资源文件中记录的对应关系。

Key:主键,表示唯一记录,若数据库访问则表示确定该属性的唯一关键字段,若读取资源文件则表示该属性对应的键值。

Connector:连接器属性,表示连接关系数据库,还是资源文件。

Configs:配置项数组,每一条配置项对应一种语言的国际化支持。Configs[0] 为英文配置信息,Configs[1] 为中文配置信息。

  • @I18n

作用在类上,声明该类需要国际化框架的支持,需要织入国际化方面进行功能增强。唯一属性 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 运行机制流程图

  • 声明 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。

  • 定义 Aspect 的 Advice

声明的 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 编程各个方面的文章。
 
相关文章

中台产品面面观
如何在互联网产品中建立中台?
什么是产品生命周期管理?
产品设计之前,如何分析业务需求和用户痛点?
 
相关文档

产品经理是怎样炼成的
APP产品规划方法
产品经理培训文档
产品生命周期管理PLM
 
相关课程

产品经理与产品管理
卓越产品经理训练营
产品需求分析与管理
基于用户体验的产品设计
 
分享到
 
 


Java 中的中文编码问题
Java基础知识的三十个经典问答
玩转 Java Web 应用开发
使用Spring更好地处理Struts
用Eclipse开发iPhone Web应用
插件系统框架分析
更多...   


Struts+Spring+Hibernate
基于J2EE的Web 2.0应用开发
J2EE设计模式和性能调优
Java EE 5企业级架构设计
Java单元测试方法与技术
Java编程方法与技术


Struts+Spring+Hibernate/EJB+性能优化
华夏基金 ActiveMQ 原理与管理
某民航公司 Java基础编程到应用开发
某风电公司 Java 应用开发平台与迁移
日照港 J2EE应用开发技术框架与实践
某跨国公司 工作流管理JBPM
东方航空公司 高级J2EE及其前沿技术
更多...