站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2EE 专题基于struts+spring+ibatis的轻量级J2EE开发

基于struts+spring+ibatis的轻量级J2EE开发

摘要: 基于struts+spring+ibatis的轻量级J2EE开发
<table cellspacing="0" cellpadding="0" width="100%" border="0"><tr valign="top"><td></td><td width="8"></td><td align="right" width="180">
</td><td width="6"></td></tr><tr valign="top"><td bgcolor="#000000" colspan="5"></td></tr><tr valign="top"><td bgcolor="#ffffff" colspan="5"></td></tr></table><table cellspacing="0" cellpadding="0" width="100%" border="0"><tr valign="top"><td width="5"></td><td width="100%"><table cellspacing="0" cellpadding="0" width="168" align="right" border="0"><tr><td width="8"></td><td width="160"><table cellspacing="0" cellpadding="0" width="160" border="0"><tr><td width="160" bgcolor="#000000" height="1"></td></tr><tr><td align="center" background="/developerworks/cn/i/bg-gold.gif" height="5">内容:</td></tr><tr><td width="160" bgcolor="#666666" height="1"></td></tr><tr><td><table cellspacing="0" cellpadding="0" width="160" border="0"><tr><td>1. 前言</td></tr><tr><td height="1"></td></tr><tr><td>2. JpetStore简述</td></tr><tr><td height="1"></td></tr><tr><td>3. JPetStore的改造</td></tr><tr><td height="1"></td></tr><tr><td>4. 结束语</td></tr><tr><td height="1"></td></tr> 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的源代码,为下面的改造铺路。

剩下的部分就比较简单了,请看具体的源代码,非常清晰。

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 cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>








</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 cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


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 cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


    private static final AccountService accountService = AccountService.getInstance();

  private static final CatalogService catalogService = CatalogService.getInstance();

  
</td></tr></table>

改造后的源代码如下


<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


  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 cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


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 cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


    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 cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


    

        

            org.hsqldb.jdbcDriver

        

        

            jdbc:hsqldb:hsql://localhost/xdb

        

        

            sa

        

        

            

        

       

   

    

        

            

                classpath:com\ibatis\jpetstore\persistence\sqlmapdao\sql\sql-map-config.xml

            

        

        

            

           

    

   

    

        

            

        

    

   

    

        

            

        

    

    
</td></tr></table>

具体的语法请参照附录中的"Spring中文参考手册"。在这里只简单解释一下:

1. 我们首先创建一个数据源dataSource,在这里配置的是hsqldb数据库。如果是ORACLE数据库,driverClassName的值是"oracle.jdbc.driver.OracleDriver",URL的值类似于"jdbc:oracle:thin:@wugfMobile:1521:cdcf"。数据源现在由spring来管理,那么现在我们就可以去掉properties目录下database.properties这个配置文件了;还有不要忘记修改sql-map-config.xml,去掉对它的引用。

2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配置了ibatis映射文件的名称。dataSource属性指向了使用的数据源,这样所有使用sqlMapClient的DAO都默认使用了该数据源,除非在DAO的配置中另外显式指定。

3. TransactionManager节点。定义了事务,使用的是DataSourceTransactionManager。

4. 下面就可以定义DAO节点了,如AccountDao,它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao,使用的SQL配置从sqlMapClient中读取,数据库连接没有特别列出,那么就是默认使用sqlMapClient配置的数据源datasource。

这样,我们就把持久层改造完了,其他的DAO配置类似于AccountDao。怎么样?简单吧。这次有接口了:) AccountDao接口->AccountSqlMapDao实现。

3.5. 业务层
业务层的位置以及相关类,如下图所示:


在这个例子中只有3个业务类,我们以OrderService类为例来改造,这个类是最复杂的,其中涉及了事务。

1、在ApplicationContext配置文件中增加bean的配置:


<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


    

        

            

        

        

            

                

                    

                

                

                    

                

                

                    

                

            

        

        

            

                PROPAGATION_REQUIRED

            

        

    

    
</td></tr></table>

定义了一个OrderService,还是很容易懂的。为了简单起见,使用了嵌套bean,其实现类是com.ibatis.jpetstore.service.OrderService,分别引用了ItemDao,OrderDao,SequenceDao。该bean的insert*实现了事务管理(AOP方式)。TransactionProxyFactoryBean自动创建一个事务advisor, 该advisor包括一个基于事务属性的pointcut,因此只有事务性的方法被拦截。

2、业务类的修改

以OrderService为例:


<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tr><td>


public class OrderService {

   /* Private Fields */

  private ItemDao itemDao;

  private OrderDao orderDao;

  private SequenceDao sequenceDao;

  /* Constructors */

  public OrderService() {

  }

/**

 * @param itemDao 要设置的 itemDao。

 */

public final void setItemDao(ItemDao itemDao) {

this.itemDao = itemDao;

}

/**

 * @param orderDao 要设置的 orderDao。

 */

public final void setOrderDao(OrderDao orderDao) {

this.orderDao = orderDao;

}

/**

 * @param sequenceDao 要设置的 sequenceDao。

 */

public final void setSequenceDao(SequenceDao sequenceDao) {

this.sequenceDao = sequenceDao;

}

//剩下的部分

…….

}

</td></tr></table>

红色部分为修改部分。Spring采用的是Type2的设置依赖注入,所以我们只需要定义属性和相应的设值函数就可以了,ItemDao,OrderDao,SequenceDao的值由spring在运行期间注入。构造函数就可以为空了,另外也不需要自己编写代码处理事务了(事务在配置中声明),daoManager.startTransaction();等与事务相关的语句也可以去掉了。和原来的代码比较一下,是不是处理精简了很多!可以更关注业务的实现。

4. 结束语
ibatis是一个功能强大实用的SQL Map工具,可以直接控制SQL,为系统设计提供了更大的自由空间。其提供的最新示例程序JpetStore 4.0,设计优雅,应用了迄今为止很多最佳实践和设计模式,非常适于学习以及在此基础上创建轻量级的J2EE WEB应用程序。JpetStore 4.0是基于struts的,本文在此基础上,最大程度保持了原有设计的精华以及最小的代码改动量,在业务层和持久化层引入了Spring。在您阅读了本文以及改造后的源代码后,会深切的感受到Spring带来的种种好处:自然的面向接口的编程,业务对象的依赖注入,一致的数据存取框架和声明式的事务处理,统一的配置文件…更重要的是Spring既是全面的又是模块化的,Spring有分层的体系结构,这意味着您能选择仅仅使用它任何一个独立的部分,就像本文,而它的架构又是内部一致。

参考资料

<table cellspacing="0" cellpadding="0" width="100%" border="0"><tr><td>关于作者
吴高峰,原一直在中兴通讯从事运营支撑产品的研发工作,对J2EE以及各种开源项目感兴趣。现在常德卷烟厂信息技术部从事EAI的建设。联系方式:shuwgf@21cn.com</td></tr></table></td></tr></table>

↑返回目录
前一篇: 没有EJB,J2EE还剩下什么?
后一篇: 学习J2EE第5天(Simple API for XML)