当前页面: 开发资料首页 → J2EE 专题 → 基于struts+spring+ibatis的轻量级J2EE开发
摘要: 基于struts+spring+ibatis的轻量级J2EE开发
多数IT 组织都必须解决三个主要问题:1.帮助组织减少成本 2.增加并且保持客户 3.加快业务效率。完成这些问题一般都需要实现对多个业务系统的数据和业务逻辑的无缝访问,也就是说,要实施系统集成工程,以便联结业务流程、实现数据的访问与共享。
JpetStore 4.0是ibatis的最新示例程序,基于Struts MVC框架(注:非传统Struts开发模式),以ibatis作为持久化层。该示例程序设计优雅,层次清晰,可以学习以及作为一个高效率的编程模型参考。本文是在其基础上,采用Spring对其中间层(业务层)进行改造。使开发量进一步减少,同时又拥有了Spring的一些好处…
1. 前言
JpetStore 4.0是ibatis的最新示例程序。ibatis是开源的持久层产品,包含SQL Maps 2.0 和 Data Access Objects 2.0 框架。JpetStore示例程序很好的展示了如何利用ibatis来开发一个典型的J2EE web应用程序。JpetStore有如下特点:
以下是本文用到的关键技术介绍,本文假设您已经对Struts,SpringFramewok,ibatis有一定的了解,如果不是,请首先查阅附录中的参考资料。
2. JpetStore简述
2.1. 背景
最初是Sun公司的J2EE petstore,其最主要目的是用于学习J2EE,但是其缺点也很明显,就是过度设计了。接着Oracle用J2EE petstore来比较各应用服务器的性能。微软推出了基于.Net平台的 Pet shop,用于竞争J2EE petstore。而JpetStore则是经过改良的基于struts的轻便框架J2EE web应用程序,相比来说,JpetStore设计和架构更优良,各层定义清晰,使用了很多最佳实践和模式,避免了很多"反模式",如使用存储过程,在java代码中嵌入SQL语句,把HTML存储在数据库中等等。最新版本是JpetStore 4.0。
2.2. JpetStore开发运行环境的建立
1、开发环境
2、Eclipse插件
3、示例源程序
2.3. 架构
图1 JpetStore架构图
图1 是JPetStore架构图,更详细的内容请参见JPetStore的白皮书。参照这个架构图,让我们稍微剖析一下源代码,得出JpetStore 4.0的具体实现图(见图2),思路一下子就豁然开朗了。前言中提到的非传统的struts开发模式,关键就在struts Action类和form bean类上。
struts Action类只有一个:BeanAction。没错,确实是一个!与传统的struts编程方式很不同。再仔细研究BeanAction类,发现它其实是一个通用类,利用反射原理,根据URL来决定调用formbean的哪个方法。BeanAction大大简化了struts的编程模式,降低了对struts的依赖(与struts以及WEB容器有关的几个类都放在com.ibatis.struts包下,其它的类都可以直接复用)。利用这种模式,我们会很容易的把它移植到新的框架如JSF,spring。
这样重心就转移到form bean上了,它已经不是普通意义上的form bean了。查看源代码,可以看到它不仅仅有数据和校验/重置方法,而且已经具有了行为,从这个意义上来说,它更像一个BO(Business Object)。这就是前文讲到的,BeanAction类利用反射原理,根据URL来决定调用form bean的哪个方法(行为)。form bean的这些方法的签名很简单,例如:
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
public String myActionMethod() {
//..work
return "success";
}
</td></tr></table>方法的返回值直接就是字符串,对应的是forward的名称,而不再是ActionForward对象,创建ActionForward对象的任务已经由BeanAction类代劳了。
另外,程序还提供了ActionContext工具类,该工具类封装了request 、response、form parameters、request attributes、session attributes和 application attributes中的数据存取操作,简单而线程安全,form bean类使用该工具类可以进一步从表现层框架解耦。
在这里需要特别指出的是,BeanAction类是对struts扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常新,一直在发展中。
图2 JpetStore 4.0具体实现
2.4. 代码剖析
下面就让我们开始进一步分析JpetStore4.0的源代码,为下面的改造铺路。
· Form bean类位于com.ibatis.jpetstore.presentation包下,命名规则为***Bean。Form bean类全部继承于BaseBean类,而BaseBean类实际继承于ActionForm,因此,Form bean类就是Struts的 ActionForm,Form bean类的属性数据就由struts框架自动填充。而实际上,JpetStore4.0扩展了struts中ActionForm的应用: Form bean类还具有行为,更像一个BO,其行为(方法)由BeanAction根据配置(struts-config.xml)的URL来调用。虽然如此,我们还是把Form bean类定位于表现层。
Struts-config.xml的配置里有3种映射方式,来告诉BeanAction把控制转到哪个form bean对象的哪个方法来处理。
以这个请求连接为例http://localhost/jpetstore4/shop/viewOrder.do
1. URL Pattern
<table style="BACKGROUND: #cccccc; MARGIN-LEFT: 36pt; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
name="orderBean" scope="session"
validate="false">
</td></tr></table>此种方式表示,控制将被转发到"orderBean"这个form bean对象 的"viewOrder"方法(行为)来处理。方法名取"path"参数的以"/"分隔的最后一部分。
2. Method Parameter
<table style="BACKGROUND: #cccccc; MARGIN-LEFT: 36pt; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
name="orderBean" parameter="viewOrder" scope="session"
validate="false">
</td></tr></table>此种方式表示,控制将被转发到"orderBean"这个form bean对象的"viewOrder"方法(行为)来处理。配置中的"parameter"参数表示form bean类上的方法。"parameter"参数优先于"path"参数。
3. No Method call
<table style="BACKGROUND: #cccccc; MARGIN-LEFT: 36pt; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
name="orderBean" parameter="*" scope="session"
validate="false">
</td></tr></table>此种方式表示,form bean上没有任何方法被调用。如果存在"name"属性,则struts把表单参数等数据填充到form bean对象后,把控制转发到"success"。否则,如果name为空,则直接转发控制到"success"。
这就相当于struts内置的org.apache.struts.actions.ForwardAction的功能
<table style="BACKGROUND: #cccccc; MARGIN-LEFT: 36pt; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
parameter="/order/ViewOrder.jsp " scope="session" validate="false">
</td></tr></table>剩下的部分就比较简单了,请看具体的源代码,非常清晰。
2.5. 需要改造的地方
JpetStore4.0的关键就在struts Action类和form bean类上,这也是其精华之一(虽然该实现方式是试验性,待扩充和验证),在此次改造中我们要保留下来,即控制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境),其它保持不变。要特别关注的改动是业务层和持久层,幸运的是JpetStore4.0设计非常好,需要改动的地方非常少,而且由模式可循,如下:
1. 业务层和数据层用Spring BeanFactory机制管理。
2. 业务层的事务由spring 的aop通过声明来完成。
3. 表现层(form bean)获取业务类的方法改由自定义工厂类来实现(加载spring环境)。
3. JPetStore的改造
3.1. 改造后的架构
其中红色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析。
3.2. Spring Context的加载
为了在Struts中加载Spring Context,一般会在struts-config.xml的最后添加如下部分:
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
value="/WEB-INF/applicationContext.xml" />
</td></tr></table>Spring在设计时就充分考虑到了与Struts的协同工作,通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是,因为在这里我们一点也不改动JPetStore的控制层(这是JpetStore4.0的精华之一),所以本文不准备采用此方式来加载ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具类(bean工厂类)来加载spring的配置文件,从中可以看出Spring有多灵活,它提供了各种不同的方式来使用其不同的部分/层次,您只需要用你想用的,不需要的部分可以不用。
具体的来说,就是在com.ibatis.spring包下创建CustomBeanFactory类,spring的配置文件applicationContext.xml也放在这个目录下。以下就是该类的全部代码,很简单:
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
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);
}
}
</td></tr></table>实际上就是封装了Spring 的XMLBeanFactory而已,并且Spring的配置文件只需要加载一次,以后就可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象了(例如someBean),而不需要知道具体的类。CustomBeanFactory类用于{耦合1}的解耦。
CustomBeanFactory类在本文中只用于表现层的form bean对象获得service类的对象,因为我们没有把form bean对象配置在applicationContext.xml中。但是,为什么不把表现层的form bean类也配置起来呢,这样就用不着这CustomBeanFactory个类了,Spring会帮助我们创建需要的一切?问题的答案就在于form bean类是struts的ActionForm类!如果大家熟悉struts,就会知道ActionForm类是struts自动创建的:在一次请求中,struts判断,如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中。因此formbean类的对象就不能由spring来创建,但是service类以及数据层的DAO类可以,所以只有他们在spring中配置。
所以,很自然的,我们就创建了CustomBeanFactory类,在表现层来衔接struts和spring。就这么简单,实现了另一种方式的{耦合一}的解耦。
3.3. 表现层
面分析到,struts和spring是在表现层衔接起来的,那么表现层就要做稍微的更改,即所需要的service类的对象创建上。以表现层的AccountBean类为例:
上
原来的源代码如下
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
private static final AccountService accountService = AccountService.getInstance();
private static final CatalogService catalogService = CatalogService.getInstance();
</td></tr></table>改造后的源代码如下
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
private static final AccountService accountService = (AccountService)CustomBeanFactory.getBean("AccountService");
private static final CatalogService catalogService = (CatalogService)CustomBeanFactory.getBean("CatalogService");
</td></tr></table>其他的几个presentation类以同样方式改造。这样,表现层就完成了。关于表现层的其它部分如JSP等一概不动。也许您会说,没有看出什么特别之处的好处啊?你还是额外实现了一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但您发没发现:
3.4. 持久层
在讨论业务层之前,我们先看一下持久层,如下图所示:
在上文中,我们把iface包下的DAO接口归为业务层,在这里不需要做修改。ibatis的sql配置文件也不需要改。要改的是DAO实现类,并在spring的配置文件中配置起来。
1、修改基类
所有的DAO实现类都继承于BaseSqlMapDao类。修改BaseSqlMapDao类如下:
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
public class BaseSqlMapDao extends SqlMapClientDaoSupport {
protected static final int PAGE_SIZE = 4;
protected SqlMapClientTemplate smcTemplate = this.getSqlMapClientTemplate();
public BaseSqlMapDao() {
}
}
</td></tr></table>使BaseSqlMapDao类改为继承于Spring提供的SqlMapClientDaoSupport类,并定义了一个保护属性smcTemplate,其类型为SqlMapClientTemplate。关于SqlMapClientTemplate类的详细说明请参照附录中的"Spring中文参考手册"
2、修改DAO实现类
所有的DAO实现类还是继承于BaseSqlMapDao类,实现相应的DAO接口,但其相应的DAO操作委托SqlMapClientTemplate来执行,以AccountSqlMapDao类为例,部分代码如下:
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
public List getUsernameList() {
return smcTemplate.queryForList("getUsernameList", null);
}
public Account getAccount(String username, String password) {
Account account = new Account();
account.setUsername(username);
account.setPassword(password);
return (Account) smcTemplate.queryForObject("getAccountByUsernameAndPassword", account);
}
public void insertAccount(Account account) {
smcTemplate.update("insertAccount", account);
smcTemplate.update("insertProfile", account);
smcTemplate.update("insertSignon", account);
}
</td></tr></table>就这么简单,所有函数的签名都是一样的,只需要查找替换就可以了!
3、除去工厂类以及相应的配置文件
除去DaoConfig.java这个DAO工厂类和相应的配置文件dao.xml,因为DAO的获取现在要用spring来管理。
4、DAO在Spring中的配置(applicationContext.xml)
<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-padding-alt: 3.75pt 3.75pt 3.75pt 3.75pt; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="1"><tr><td style="BORDER-RIGHT: #d4d0c8; PADDING-RIGHT: 3.75pt; BORDER-TOP: #d4d0c8; PADDING-LEFT: 3.75pt; PADDING-BOTTOM: 3.75pt; BORDER-LEFT: #d4d0c8; PADDING-TOP: 3.75pt; BORDER-BOTTOM: #d4d0c8; BACKGROUND-COLOR: transparent">
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
org.hsqldb.jdbcDriver
jdbc:hsqldb:hsql://localhost/xdb
sa