1 J2EE多层应用分析
1.1 J2EE层次结构
J2EE的三层结构在业界是指表示层(Presentation),业务逻辑层(Business logic)以及基础架构层(Infrastructure)。这样的划分当然是经典的,但是在实际项目中,往往会对三层体系结构做一些扩展来满足项目的需要。一个最常用的扩展就是将三层体系扩展为五层体系,即表示层(Presentation)、控制/中介层(Controller/Mediator) 、领域层(Domain)、 数据持久层(Data Persistence) 和数据源层(Data Source)。它其实是在三层架构中增加了两个中间层。控制/中介层位于表示层和领域层之间,数据持久层位于领域层和基础架构层之间。而轻量级架构Struts + Spring + Ibatis可以实现J2EE多层结构,Struts 用于表示层、控制层,Spring 用于业务处理层,而Ibatis 用于数据持久层。
1.2 Struts 框架
Struts是一个基于Sun J2EE平台的MVC框架,主要是采用Servlet和JSP技术来实现的。是开发Web应用程序的开放源码的Framework。Struts把Servlet、JSP、自定义标签和信息资源(message resources)整合到一个统一的框架中,开发人员利用其进行开发时不用再自己编码实现全套MVC模式,极大的节省了时间,Struts包括如下的主要功能:
☆ 包含一个controller servlet,能将用户的请求发送到相应的Action对象。
☆ JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。
☆ 提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。
1.3 Spring 框架
Spring是一个目前非常活跃的开源项目;它是一个基于IoC(Inversion of Control)和AOP(Aspect-Oriented Programming)的,轻量级的,多层j2ee系统的框架,但它不强迫你必须在每一层中必须使用Spring,它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;它实现了很优雅的MVC,对不同的数据访问技术提供了统一的接口,采用IoC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现Transcation Managment,等等[ 1 ]。
Spring虽然提供了MVC Web框架的解决方案,但是也能与其他的web框架相结合使用,如struts、Webwork、JFS等。Spring也可以与其他持久层结构相结合,如:jdbc、Hibernate、Ibatis等,能够使用AOP的技术提供事务处理等功能。
1.4 数据关系映射Ibatis
Ibatis 提供了ORM机制,对业务逻辑实现人员而言,面对的是纯粹的Java对象, 这一层与通过Hibernate 实现ORM 而言基本一致,而对于具体的数据操作,Hibernate 会自动生成SQL 语句,而Ibatis 则要求开发者编写具体的SQL 语句。相对Hibernate等 “全自动”ORM机制而言,Ibatis 以SQL开发的工作量和数据库移植性上的让步,为系统设计提供了更大的自由空间。作为“全自动”ORM 实现的一种有益补充,Ibatis 的出现显得别具意义[ 2 ]。
2 J2EE轻量级架构
2.1 架构
图1 框架示意图
2.2 View层
在视图层主要使用Struts结构的技术。
☆ TagLib
在jsp的前端页面中使用Struts提供的标签完成页面数据逻辑的组织与显示。如(html,bean,logic等)
☆ 国际化的消息处理功能
在jsp页面中不出现特定语言的字符串描述,将这些统一的字符串信息统一的提取到文件中,通过Struts的Bean标签提取。便于整个系统的语言变更与组织。
☆ 界面组合功能Tiles
页面中将使用Struts的技术,统一组织页面的结构,便于系统改版、维护。
控制层中依然使用Struts结构。此层将接受、处理View发送的请求,在对信息初步的验证、处理后,将把此请求委派给后端的业务处理层去处理。等待业务处理完成之后,将把处理结果传递给View层进行表现。
在该层中使用Service模式,提供给Control层必要的处理方法。
在接收到Control层的业务处理请求后,按照业务规范、算法对数据进行处理。将处理结果返回给Control层。
本层属于低级的数据处理层,将完成具体的数据处理工作,如数据的增加,更新,查询,删除等。
本层使用DAO(Data Access Object)的设计模式[ 3 ],屏蔽多数据库对上层应用的影响,并使用Ibatis的ORMapping技术,降低数据操作的复杂程度。
3 数据持久层解决方案
3.1 数据持久层
将数据持久单独作为J2EE体系一个层提出来,表面上是因为数据持久性是企业实际开发中比较棘手的一个方面,数据持久层设计的成功与否往往对项目起着至关重要的影响,单独将其提出来以便在开发中能够避免它的设计草率。究其最深刻的内因,则是在对象范例和关系范例这两大领域之间“阻抗不匹配”。对象范例基于软件工程的一些原理,例如耦合、聚合和封装,而关系范例则基于数学原理,特别是集合论的原理。两种不同的理论基础导致各自有不同的优缺点。而且,对象范例侧重于从包含数据和行为的对象中构建应用程序,而关系范例则主要针对数据的存储。当为访问而寻找一种合适的方法时,“阻抗不匹配”就成了主要矛盾:使用对象范例,通过它们的关系来访问对象,而使用关系范例,则通过复制数据来连接表中的行。这种基本的差异导致两种范例的结合并不理想,于是有人提出对象数据库以希望解决这个问题。但是,关系数据库技术已经发展得相当成熟,占据了数据库市场90%以上的份额,对象数据库的普及尚需时日。数据持久层就是要在对象-关系数据库之间提供一个成功的企业级别的映射解决方案,尽最大可能弥补这两种范例之间的差异。
3.2 J2EE体系解决方案
3.2.1 EJB
CMP (容器管理持久)实体Bean,提供健壮的数据持久性。Bean 容器处理大部分的数据完整性、资源管理和并发性功能,从而使开发人员关注业务逻辑和数据处理,而不是这些低级细节。使用Bean 管理的持久性(Bean Managed Persistence,BMP)实体Bean 时,开发人员编写持久性代码而容器确定何时执行该代码。使用容器管理的持久性 ( Container Managed Persistence,CMP)实体Bean时,容器生成持久性代码并管理持久性逻辑[ 4 ]。
3.2.2 JDO
Java 数据对象 (JDO) 是一个存储Java对象的规范[ 5 ]。它已经被JCP组织定义成JSR12规范。规范的两个主要目的是提供数据处理和访问机制的API以及允许规范的实现作为应用服务器的一部分。Java 数据对象是最新的持久性规范。JDO 提供了面向对象的持久数据存储。开发人员使用 POJO(无格式普通 Java 对象,Plain Ordinary Java Object)来装入和存储持久数据。
3.2.3 ORM
ORM 具有自我存储到关系数据库的能力,对对象的改变能够直接得以存储,而不考虑数据库存取代码。这样,把全部精力集中到对对象和类进行编程,解决业务问题。在整个系统中除了这一个层次,没有一句数据库存取代码。其中,Hibernate、Ibatis等作为 ORM中最好的开源工具,受到数量众多的程序员的拥护。
4 基于轻量级架构的应用案例分析
某大型水利信息系统需要使用J2EE 平台以满足分布式处理、事务管理和安全的要求,使整个软件呈现分层的体系结构,支持多种数据库的扩展,支持多语言界面的调整,支持页面整体布局的可扩展性调整。封装业务逻辑,使得在外部条件变化时,将影响尽可能的降低,即采用了基于J2EE的轻量级架构。图2是信息系统的体系结构,图3是系统包结构
图2 体系结构图
图3 系统包结构
图3中箭头表示软件包之间的依赖关系。独立的包会被Spring、struts、Ibatis框架结构间接的与其他软件包关联。
4.1 系统剖析
4.1.1 包结构描述
☆ com.water.query.dao:该包内的类将引入Ibatis的重要组件,使系统能够感知功能操作的接口与接口实现类的关系,这些信息是Ibatis自动映射的前提条件之一。
☆ com.water.query.dao.sqlmapdao:该包是数据访问的核心,使用Spring结构的数据层功能引进对Ibatis的支持
☆ com.water.query.dao.iface :该包规定了所有业务的访问接口,只有在此包的接口中定义的方法才会被用户使用。是本系同中所有原子数据操作的定义点。
☆ Ibatis :该包是由Ibatis数据存储层结构定义。由支持机构提供支持。
☆ com.water.query.dao.sqlmapdao.sql :该包定义了所有操作的SQL(Sequence Query Language)语句,以及SQL语句与具体值对象的映射关系。
☆ com.water.query.form :该包定义了本系统中用于数据传输的类。这些类实例是Web界面层与业务逻辑实现层之间的桥梁,起着传输数据的功能。
☆ com.water.query.service : 该包是本系统引入Spring结构的入口点。通过该入口,系统中其他成员可以通过IoC(依赖倒转)功能访问具体业务的Service对象。
☆ com.water.query.condition :该包内定义了一系列简单JavaBean,这些JavaBean将携带SQL需要的参数穿行于软件的Control层与Model层之间。
☆ com.water.log :该包中定义了本系统使用的log组件。
☆ com.water.query.domain.baseinfo :该包定义了所有基本信息查询模块将要使用的数据传输类。
4.1.2 控制层
在视图层和控制层中采用了Struts框架,在传统的Struts用法中,我们一般都有几个Action Bean和相应的form bean ,但在此设计中并不拘泥于Struts的传统固定用法,这里只用了一个自定义Action类,并且在form bean类的定义上也是创新的。
非传统的Struts开发模式,关键就在Struts Action类和form bean类上。Struts Action类只有一个:BeanAction,这与传统的struts编程方式很不同。BeanAction类是一个通用类,利用反射原理[ 6 ],根据URL来决定调用form bean的哪个方法。BeanAction大大简化了struts的编程模式,降低了对struts的依赖。利用这种模式,我们会很容易的把它移植到新的框架如JSF,Spring。
这样重心就转移到form bean上了,它已经不是普通意义上的form bean了。它不仅仅有数据和校验/重置方法,而且已经具有了行为,从这个意义上来说,它更像一个BO(Business Object),Struts-config.xml的配置里有3种映射方式,来告诉BeanAction把控制转到哪个form bean对象的哪个方法来处理。form bean的这些方法的签名很简单,例如:
public String myActionMethod(){
//..work
return "success";
}
方法的返回值直接就是字符串,对应的是forward的名称,而不再是ActionForward对象,创建ActionForward对象的任务已经由BeanAction类代劳了。
4.1.3 业务层
利用Spring Framework 的BeanFactory机制,采用自定义的工具类(bean工厂类)来加载spring的配置文件,从中可以看出Spring有多灵活,它提供了各种不同的方式来使用其不同的部分/层次,您只需要用你想用的,不需要的部分可以不用。
具体的来说,创建CustomBeanFactory类,Spring的配置文件applicationContext.xml。以下就是该类的全部代码,很简单:
public final class CustomBeanFactory {
static XmlBeanFactory factory = null;
static {
Resource is = new
InputStreamResource( CustomBeanFactory.class.
getResourceAsStream("applicationContext.xml"));
factory = new XmlBeanFactory(is);
}
public static Object getBean(String beanName){
return factory.getBean(beanName);
}
}
实际上就是封装了Spring 的XMLBeanFactory而已,并且Spring的配置文件只需要加载一次,以后就可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象了(例如someBean),而不需要知道具体的类。CustomBeanFactory类用于{耦合1}的解耦。CustomBeanFactory类用于表现层的form bean对象获得service类的对象。
4.1.4 数据持久层的配置
4.1.4.1 Spring 配置文件
Spring在设计时就充分考虑到了与Struts的协同工作,并且对Ibatis 有很好的支持,在此轻量级架构中,不需要对Ibatis单独进行配置,只需对 Spring 进行配置就以足够。以下就是Spring的配置文件applicationContext.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="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value>
</property>
<property name="url"><value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value>
</property>
<property name="username"><value>test</value></property>
<property name="password"><value>changeit</value></property>
</bean>
<bean id="sqlMapClient"class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation"><value>SqlMapConfig.xml</value></property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref local="dataSource"/></property>
</bean>
<bean id="userDAO" class="net.xiaxin.dao.UserDAO">
<property name="dataSource"><ref local="dataSource" /></property>
<property name="sqlMapClient"><ref local="sqlMapClient" /></property>
</bean>
</beans>
可以看到:
1. sqlMapClient节点
sqlMapClient节点实际上配置了一个sqlMapClient的创建工厂类。
configLocation属性配置了ibatis映射文件的名称。
2. transactionManager节点
transactionManager采用了Spring中的DataSourceTransactionManager。
3. userDAO节点
对应的,UserDAO需要配置两个属性,sqlMapClient和DataSource,
sqlMapClient将从指定的DataSource中获取数据库连接。
4.1.4.2 Ibatis映射文件
sqlMapConfig.xml:
<sqlMapConfig>
<sqlMap resource="net/xiaxin/dao/entity/user.xml"/>
</sqlMapConfig>
user.xml为:
<sqlMap namespace="User">
<typeAlias alias="user" type="net.xiaxin.dao.entity.User" />
<insert id="insertUser" parameterClass="user">
INSERT INTO users ( username, password) VALUES ( #username#,
#password# )
</insert>
</sqlMap>
UserDAO.java:
public class UserDAO extends SqlMapClientDaoSupport implements
IUserDAO {
public void insertUser(User user) {
getSqlMapClientTemplate().update("insertUser", user);
}
}
SqlMapClientDaoSupport是Spring中面向ibatis的辅助类,它负责调度DataSource、
SqlMapClientTemplate完成ibatis操作,而DAO则通过对此类进行扩展获得上述功能。上面配置文件中针对UserDAO的属性设置部分,其中的属性也是继承自于这个基类。SqlMapClientTemplate对传统SqlMapClient调用模式进行了封装,简化了上层访问代码。
5 总结
从软件层次结构的角度来说,软件的框架要具有较高的伸缩性和可扩展性,本文所讨论的J2EE轻量级架构,由于它采用了Struts框架,因而它的模块化设计得到了很好的应用,层次非常清晰,具有很好的可复用度,但是,同时,此架构存在可伸缩性问题,架构中明显存在可伸缩性问题的设计是Action。控制器ActionServlet 对每个Action 类只创建一个实例,所有对该Action 类的请求都用这个实例进行处理,因此,它是无状态的、多线程共享的。这意味着Action 实例的数量是一个常数,不能随工作量增加而增加。此外,反射的利用是对架构扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常新,一直在发展中。数据持久层中,Ibatis的应用给人一种耳目一新的感觉,但它也存在着一些不足之处,例如相对于Hibernate ,Ibatis不能直接生成sql语句,需要人工编写。
轻量级架构结合Struts、Spring、Ibatis ,充分发挥了三者的优点, 基于轻量级框架的J2EE 架构开发简洁、结构清晰,有很好的可扩展性和可维护性,非常适于面向对象的设计与开发。
参考文献(References)
[1] Expert One-on-One J2EE Design and Development (美)Rod ohnson,Juergen Hoeller
机械工业出版社 2005-8-1
[2] What is iBATIS? http://ibatis.apache.org/ 2005
[3] iBatis DAO http://www.onjava.com/pub/a/onjava/2005/08/10/ibatisdao.html 2005
[4] Roman E. Mastering EJB (2nd Edition). 北京: 机械工业出版社, 2003-01
[5] JDO:SunJDOSpecification[EB/OL].
http://www.jcp.org/aboutJava/communityprocess/first/jsr012/index.html , 2003
[6] Java编程思想(第2版)(美)Bruce Eckel机械工业出版社2002-9-1