`
justin8335
  • 浏览: 28895 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

常见SSH的架构设计策略(一)

阅读更多

(转载自)轻量级J2EE企业应用实战——Struts+Spring+Hibernate整合开发         李刚

常见的架构设计策略

目前流行的轻量级J2EE应用的架构比较一致,采用的技术也比较一致,通常使用Spring作为核心,向上整合MVC框架,向下整合ORM框架。使用Spring的IOC容器来管理各组件之间的依赖关系时,Spring的声明事务将负责业务逻辑层对象方法的事务管理。

但在固定的技术组合上,依然可能存在小的变化。下面依次讨论可能存在的架构策略。

8.4.1  贫血模式

贫血模式是最常用的设计架构,也是最容易理解的架构。为了让读者通过本书顺利进入轻量级J2EE企业应用开发,本书的第9章及第10章的范例都将采用这种简单的架构模式。

所谓贫血,指Domain Object只是单纯的数据类,不包含业务逻辑方法,即每个Domain Object类只包含基本的setter和getter方法。所有的业务逻辑都由业务逻辑组件实现,这种Domain Object就是所谓的贫血的Domain Object,采用这种Domain Object的架构即所谓的贫血模式。

下面以第9章的消息发布系统的部分代码为例,介绍贫血模式。

在贫血模式里,所有的Domain Object只是单纯的数据类,只包含每个属性的setter和getter方法,如下是两个持久化类。

第一个Domain Object是消息,其代码如下:

java 代码
  1. public class News extends BaseObject implements Serializable   
  2.   
  3. {   
  4.   
  5.     //主键   
  6.   
  7.     private Long id;   
  8.   
  9.     //消息标题   
  10.   
  11.     private String title;   
  12.   
  13.     //消息内容   
  14.   
  15.     private String content;   
  16.   
  17.     //消息的发布时间   
  18.   
  19.     private Date postDate;   
  20.   
  21.     //消息的最后修改时间   
  22.   
  23.     private Date lastModifyDate;   
  24.   
  25.     //消息所属分类   
  26.   
  27.     private Category category;   
  28.   
  29.     //消息对应的消息回复   
  30.   
  31.     private Set newsReviews;   
  32.   
  33.     //无参数的构造器   
  34.   
  35.     public News() {   
  36.   
  37.     }   
  38.   
  39.     //消息回复对应的getter方法   
  40.   
  41.     public Set getNewsReviews() {   
  42.   
  43.         return newsReviews;   
  44.   
  45.     }   
  46.   
  47.     //消息回复对应的setter方法   
  48.   
  49.     public void setNewsReviews(Set newsReviews) {   
  50.   
  51.         this.newsReviews = newsReviews;   
  52.   
  53.     }   
  54.   
  55.     //消息分类对应的getter方法   
  56.   
  57.     public Category getCategory() {   
  58.   
  59.         return category;   
  60.   
  61.     }   
  62.   
  63.     //消息分类对应的setter方法   
  64.   
  65.     public void setCategory(Category category) {   
  66.   
  67.         this.category = category;   
  68.   
  69.     }   
  70.   
  71.     //消息最后修改时间的getter方法   
  72.   
  73.     public Date getLastModifyDate() {   
  74.   
  75.         return lastModifyDate;   
  76.   
  77.     }   
  78.   
  79.     //消息最后修改时间的setter方法   
  80.   
  81.     public void setLastModifyDate(Date lastModifyDate) {   
  82.   
  83.         this.lastModifyDate = lastModifyDate;   
  84.   
  85.     }   
  86.   
  87.     //消息发布时间的getter方法   
  88.   
  89.     public Date getPostDate() {   
  90.   
  91.         return postDate;   
  92.   
  93.     }   
  94.   
  95.     //消息发布时间的setter方法   
  96.   
  97.     public void setPostDate(Date postDate) {   
  98.   
  99.         this.postDate = postDate;   
  100.   
  101.     }   
  102.   
  103.     //消息内容对应的getter方法   
  104.   
  105.     public String getContent() {   
  106.   
  107.         return content;   
  108.   
  109.     }   
  110.   
  111.     //消息发布者对应的setter方法   
  112.   
  113.     public void setContent(String content) {   
  114.   
  115.         this.content = content;   
  116.   
  117.     }   
  118.   
  119.     //消息主键对应的getter方法   
  120.   
  121.     public Long getId() {   
  122.   
  123.         return id;   
  124.   
  125.     }   
  126.   
  127.     //消息主键对应的setter方法   
  128.   
  129.     public void setId(Long id) {   
  130.   
  131.         this.id = id;   
  132.   
  133.     }   
  134.   
  135.     //消息标题对应的getter方法   
  136.   
  137.     public String getTitle() {   
  138.   
  139.         return title;   
  140.   
  141.     }   
  142.   
  143.     //消息标题对应的setter方法   
  144.   
  145.     public void setTitle(String title) {   
  146.   
  147.         this.title = title;   
  148.   
  149.     }   
  150.   
  151.     //Domain Object重写equals方法   
  152.   
  153.     public boolean equals(Object object) {   
  154.   
  155.         if (!(object instanceof News)) {   
  156.   
  157.             return false;   
  158.   
  159.         }   
  160.   
  161.         News rhs = (News) object;   
  162.   
  163.         return this.poster.equals(rhs.getPoster())   
  164.   
  165.                 && this.postDate.equals(rhs.getPostDate());   
  166.   
  167.     }   
  168.   
  169.     //Domain Object重写的hashCode方法   
  170.   
  171.     public int hashCode() {   
  172.   
  173.         return this.poster.hashCode() + this.postDate.hashCode();   
  174.   
  175.     }   
  176.   
  177.     //Domain Object重写toString方法   
  178.   
  179.     public String toString() {   
  180.   
  181.     return new ToStringBuilder(this).append("id"this.id).append("title",   
  182.   
  183.             this.title).append("postDate"this.postDate).append("content",   
  184.   
  185.             this.content).append("lastModifyDate"this.lastModifyDate)   
  186.   
  187.                 .append("poster"this.poster)   
  188.   
  189.                 .append("category"this.category).append("newsReviews",   
  190.   
  191.                         this.newsReviews).toString();   
  192.   
  193.     }   
  194.   
  195. }   
  196.   

第二个Domain Object是消息对应的回复,其代码如下:

java 代码
  1. public class NewsReview extends BaseObject   
  2.   
  3. {   
  4.   
  5.     //消息回复的主键   
  6.   
  7.     private Long id;   
  8.   
  9.     //消息回复的内容   
  10.   
  11.     private String content;   
  12.   
  13.     //消息回复的回复时间   
  14.   
  15.     private Date postDate;   
  16.   
  17.     //回复的最后修改时间   
  18.   
  19.     private Date lastModifyDate;   
  20.   
  21.     //回复的对应的消息   
  22.   
  23.     private News news;   
  24.   
  25.     //消息回复的构造器   
  26.   
  27.     public NewsReview() {   
  28.   
  29.     }   
  30.   
  31.     //回复内容对应的getter方法   
  32.   
  33.     public String getContent() {   
  34.   
  35.         return content;   
  36.   
  37.     }   
  38.   
  39.     //回复内容对应的setter方法   
  40.   
  41.     public void setContent(String content) {   
  42.   
  43.         this.content = content;   
  44.   
  45.     }   
  46.   
  47.     //回复主键对应的setter方法   
  48.   
  49.     public Long getId() {   
  50.   
  51.         return id;   
  52.   
  53.     }   
  54.   
  55.     //回复主键对应的setter方法   
  56.   
  57.     public void setId(Long id) {   
  58.   
  59.         this.id = id;   
  60.   
  61.     }   
  62.   
  63.     //回复的最后修改时间对应的getter方法   
  64.   
  65.     public Date getLastModifyDate() {   
  66.   
  67.         return lastModifyDate;   
  68.   
  69.     }   
  70.   
  71.     //回复的最后修改时间对应的setter方法   
  72.   
  73.     public void setLastModifyDate(Date lastModifyDate) {   
  74.   
  75.         this.lastModifyDate = lastModifyDate;   
  76.   
  77.     }   
  78.   
  79.     //回复对应的消息的getter方法   
  80.   
  81.     public News getNews() {   
  82.   
  83.         return news;   
  84.   
  85.     }   
  86.   
  87.     //回复对应的消息的setter方法   
  88.   
  89.     public void setNews(News news) {   
  90.   
  91.         this.news = news;   
  92.   
  93.     }   
  94.   
  95.     //回复发布时间的getter方法   
  96.   
  97.     public Date getPostDate() {   
  98.   
  99.         return postDate;   
  100.   
  101.     }   
  102.   
  103.     //回复发布时间的setter方法   
  104.   
  105.     public void setPostDate(Date postDate) {   
  106.   
  107.         this.postDate = postDate;   
  108.   
  109.     }   
  110.   
  111.     //Domain Object重写的equals方法   
  112.   
  113.     public boolean equals(Object object) {   
  114.   
  115.         if (!(object instanceof NewsReview)) {   
  116.   
  117.             return false;   
  118.   
  119.         }   
  120.   
  121.         NewsReview rhs = (NewsReview) object;   
  122.   
  123.         return this.poster.equals(rhs.getPoster()) &&    
  124.   
  125. this.postDate.equals(rhs.getPostDate());   
  126.   
  127.         /*return new EqualsBuilder().append(this.news, rhs.news).append(  
  128.  
  129.             this.content, rhs.content).append(this.postDate, rhs.postDate)  
  130.  
  131.                 .append(this.lastModifyDate, rhs.lastModifyDate).append(  
  132.  
  133.                         this.id, rhs.id).append(this.poster, rhs.poster)  
  134.  
  135.                 .isEquals();  
  136.  
  137. */  
  138.   
  139.     }   
  140.   
  141.     //Domain Object对应的hashCode方法   
  142.   
  143.     public int hashCode() {   
  144.   
  145.         return this.poster.hashCode() + this.postDate.hashCode();   
  146.   
  147.         /*return new HashCodeBuilder(-1152635115, 884310249).append(this.news)  
  148.  
  149.                 .append(this.content).append(this.postDate).append(  
  150.  
  151.                         this.lastModifyDate).append(this.id)  
  152.  
  153.                 .append(this.poster).toHashCode();  
  154.  
  155. */  
  156.   
  157.     }   
  158.   
  159.     //Domain Object对应的toString方法   
  160.   
  161.     public String toString() {   
  162.   
  163.         return new ToStringBuilder(this).append("id"this.id).append(   
  164.   
  165.             "postDate"this.postDate).append("lastModifyDate",   
  166.   
  167.                 this.lastModifyDate).append("content"this.content).append(   
  168.   
  169.                 "poster"this.poster).append("news"this.news).toString();   
  170.   
  171.     }   
  172.   
  173. }   
  174.   

从上面贫血模式的Domain Object可看出,其类代码中只有setter和getter方法,这种Domain Object只是单纯的数据体,类似于C的数据结构。虽然它的名字是Domain Object,却没有包含任何业务对象的相关方法。Martin Fowler认为,这是一种不健康的建模方式,Domain Model既然代表了业务对象,就应该包含相关的业务方法。从语言的角度上来说,Domain Model在这里被映射为Java对象(一般都是ORM), Java对象应该是数据与动作的集合,贫血模型相当于抛弃了Java面向对象的性质。

Rod Johnson和Martin Fowler一致认为:贫血的Domain Object实际上以数据结构代替了对象。他们认为Domain Object应该是个完整的Java 对象, 既包含基本的数据,也包含了操作数据相应的业务逻辑方法。

下面是NewsDAOHibernate的源代码,该DAO对象用于操作News对象:

java 代码
  1. //NewsDAOHibernate继承HibernateDaoSupport,实现NewsDAO接口   
  2.   
  3. public class NewsDAOHibernate extends HibernateDaoSupport implements NewsDAO   
  4.   
  5. {   
  6.   
  7.     //根据主键加载消息   
  8.   
  9.     public News getNews(Long id)    
  10.   
  11.     {   
  12.   
  13.           News news = (News) getHibernateTemplate().get(News.class, id);   
  14.   
  15.           if (news == null) {   
  16.   
  17.                throw new ObjectRetrievalFailureException(News.class, id);      
  18.   
  19.           }   
  20.   
  21.         return news;   
  22.   
  23.     }   
  24.   
  25.     //保存新的消息   
  26.   
  27.     public void saveNews(News news) {   
  28.   
  29.           getHibernateTemplate().saveOrUpdate(news);   
  30.   
  31.     }   
  32.   
  33.     //根据主键删除消息   
  34.   
  35.     public void removeNews(Long id)   
  36.   
  37.     {   
  38.   
  39.           getHibernateTemplate().delete(getNews(id));   
  40.   
  41.     }   
  42.   
  43.     //查找全部的消息   
  44.   
  45.     public List findAll()   
  46.   
  47.     {   
  48.   
  49.         getHibernateTemplate().find("from News"));   
  50.   
  51.     }   
  52.   
  53. }   
  54.   

既然DAO对象完成具体的持久化操作,因此基本的CRUD操作都应该在DAO对象中实现。但DAO对象应该包含多少个查询方法,并不是确定的。因此,根据业务逻辑的不同需要, 不同的DAO对象可能有数量不等的查询方法。

对于现实中News,应该包含一个业务方法(addNewsReviews方法)。在贫血模式下,News类的代码并没有包含该业务方法,只是将该业务方法放到业务逻辑对象中实现,下面是业务逻辑对象实现addNewsReviews的代码:

java 代码
  1. public class FacadeManagerImpl implements FacadeManager   
  2.   
  3. {   
  4.   
  5.     //业务逻辑对象依赖的DAO对象   
  6.   
  7.     private CategoryDAO categoryDAO;   
  8.   
  9.     private NewsDAO newsDAO;   
  10.   
  11.     private NewsReviewDAO newsReviewDAO;   
  12.   
  13.     private UserDAO userDAO;   
  14.   
  15.     //...此处还应该增加依赖注入DAO对象必需的setter方法   
  16.   
  17.     //...此处还应该增加其他业务逻辑方法   
  18.   
  19.     //下面是增加新闻回复的业务方法   
  20.   
  21.     public NewsReview addNewsReview(Long newsId , String content)   
  22.   
  23.     {   
  24.   
  25.         //根据新闻id加载新闻   
  26.   
  27.           News news = newsDao.getNews(newsId);   
  28.   
  29.         //以默认构造器创建新闻回复   
  30.   
  31.           NewsReview review = new NewsReview();   
  32.   
  33.         //设置新闻与新闻回复之间的关联   
  34.   
  35.           review.setNews(news);   
  36.   
  37.         //设置新闻回复的内容   
  38.   
  39.           review.setContent(content);   
  40.   
  41.         //设置回复的回复时间   
  42.   
  43.           review.setPostDate(new Date());   
  44.   
  45.         //设置新闻回复的最后修改时间   
  46.   
  47.           review.setLastModifyDate(new Date());   
  48.   
  49.         //保存回复   
  50.   
  51.           newsReviewDAO.saveNewsReview(review);   
  52.   
  53.           return review;   
  54.   
  55.     }   
  56.   
  57. }   
  58.   

在贫血模式下,业务逻辑对象正面封装了全部的业务逻辑方法,Web层仅与业务逻辑组件交互即可,无须访问底层的DAO对象。Spring的声明式事务管理将负责业务逻辑对象方法的事务性。

在贫血模式下,其分层非常清晰。Domain Object 并不具备领域对象的业务逻辑功能,仅仅是ORM框架持久化所需的POJO,仅是数据载体。贫血模型容易理解,开发便捷,但严重背离了面向对象的设计思想,所有的Domain Object并不是完整的Java对象。

总结起来,贫血模式存在如下缺点:

— 项目需要书写大量的贫血类,当然也可以借助某些工具自动生成。

—  Domain Object的业务逻辑得不到体现。由于业务逻辑对象的复杂度大大增加,许多不应该由业务逻辑对象实现的业务逻辑方法,完全由业务逻辑对象实现,从而使业务逻辑对象的实现类变得相当臃肿。

贫血模式的优点是:开发简单、分层清晰、架构明晰且不易混淆;所有的依赖都是单向依赖,解耦优秀。适合于初学者及对架构把握不十分清晰的开发团队。

分享到:
评论

相关推荐

    SSH架构的通用在线考试系统

    《SSH架构下的通用在线考试系统详解》 SSH(Struts + Spring + Hibernate)是一种常见的Java Web应用程序开发框架,因其灵活性、高效性和可扩展性而被广泛应用于构建各种类型的Web应用,其中包括在线考试系统。本...

    SSH架构的搭建过程

    SSH架构,全称为Struts+Spring+Hibernate,是一种在Java Web开发中常见的开源框架组合,用于构建企业级应用。SSH架构的主要目标是提供一个松耦合、可重用且易于维护的开发环境。 1. **Struts框架**: Struts是MVC...

    SSH架构性能优化方案.pdf

    SSH架构,即Struts、Spring和Hibernate的组合,是Java Web开发中的常见选择。本篇文档将聚焦于SSH架构的服务器端性能优化,探讨如何通过改进Java编程习惯来提升系统性能。 首先,关于字符串操作的优化,应当避免...

    基于SSH的基础架构设计.doc

    在SSH架构中,Spring 主要负责管理Bean,包括Struts2的Action类和Hibernate的Session工厂等。它还支持事务管理,可以协调不同数据源的操作,确保数据的一致性。 **3. Hibernate** Hibernate 是一个对象关系映射...

    SOA-SSH分层架构的设计与应用word版本.pdf

    SOA(Service-Oriented Architecture,面向服务架构)是一种软件设计策略,它强调将业务功能分解为独立的服务,这些服务可以通过网络进行互操作,以创建灵活且可复用的系统。SSH则是Java开发中的一种常见分层架构,...

    ssh架构的构建

    ### SSH架构的构建知识点 #### 一、SSH框架概述 SSH是Spring、Struts和Hibernate三个开源框架的首字母缩写。这三个框架分别是Java企业级应用开发中的三个关键部分:业务逻辑层处理(Spring)、MVC模式实现(Struts...

    票务系统JAVA 采用SSH架构

    总的来说,基于SSH架构的Java票务系统是一个综合性的项目,涵盖了Web开发、数据库管理、业务逻辑处理等多个方面的知识。在实际开发过程中,还需要考虑系统的可维护性、可扩展性和性能,以满足不同规模和需求的票务...

    arm开发板-linux-ssh移植工具文件

    2. **选择SSH实现**:常见的SSH实现有OpenSSH,它是一个开源项目,包含客户端和服务器组件。我们需要下载适合ARM架构的OpenSSH源码,并根据开发板的具体版本进行配置。 3. **配置编译**:使用`./configure`脚本来...

    log4j在ssh架构下的配置使用.docx

    **三、SSH架构下使用Log4j的原因** SSH(Struts + Spring + Hibernate)是Java Web开发中常见的三层架构,其中Struts负责表现层,Spring管理业务逻辑,Hibernate处理数据持久化。由于SSH框架涉及多个组件和层次,...

    SSH2框架搭建实例源码

    SSH2框架,全称为Spring、Struts2和Hibernate2的集成框架,是Java Web开发中的一个常见组合。这个实例源码使用的是Spring 3.2、Struts2 2.3.4和Hibernate 4.2这三个框架的较新版本,提供了一个基础的用户登录和用户...

    SSH2实现的商城系统

    SSH2(Struts2 + Spring + Hibernate2)是一种常见的Java Web开发框架组合,它整合了三个强大的开源项目,用于构建高效、可维护的企业级应用程序。在这个"SSH2实现的商城系统"中,我们主要探讨以下几个核心知识点: ...

    ssh.rar_SSH管理系统_ssh 管理系统_ssh bbs_ssh论坛系统_基于ssh 的论坛

    SSH,全称是Struts 2 + Spring + Hibernate,是一种常见的Java Web开发框架组合,用于构建高效、可扩展的企业级应用程序。在这个名为"ssh.rar"的压缩包中,包含了一个基于SSH框架的论坛管理系统,旨在提供一个简洁而...

    SSH做的BBS

    SSH是Java Web开发中常见的三大框架,它们分别负责不同的职责: 1. **Struts2**:这是一个MVC(Model-View-Controller)框架,用于处理HTTP请求并控制应用程序的流程。Struts2提供了丰富的拦截器机制,可以方便地...

    微博系统毕业设计论文及源代码(SSH)

    【描述】提到这是一个“优秀毕业设计”,意味着该设计不仅完成了基本的功能实现,还可能在用户体验、性能优化或系统架构上有出色的表现。毕业设计通常包括需求分析、系统设计、编码实现、测试以及论文撰写等多个环节...

    在SSH中使用事物包括SSH的搭建和配置;事物的配置;注释详细

    SSH是一个常见的Java Web开发栈,用于构建高效、模块化且可维护的应用程序。下面我们将深入探讨SSH中的事务管理和配置。 首先,SSH中的每一个组件都有其在事务处理中的角色: 1. **Struts2**:作为MVC框架,主要...

    SSH当当网项目源代码

    "SSH当当网项目源代码"是一个典型的Java Web开发项目,使用了Struts2、Spring和Hibernate这三大流行框架的组合,...通过深入研究这个项目,可以提升对SSH框架的掌握,同时也能了解实际项目开发中的常见问题和解决策略。

    java程序员ssh面试常见题

    ### Java程序员SSH面试常见题详解 #### 1. Action是什么?如何证明Action是线程安全的? - **知识点概述**:在Struts框架中,Action是处理用户请求的核心组件之一,它负责执行业务逻辑并返回相应的结果。由于...

    全注解ssh开发

    在IT行业中,SSH(Struts2 + Spring + Hibernate)是一个非常常见的Java Web开发框架组合,用于构建高效、可维护的企业级应用。这个“全注解SSH开发”主题着重于使用注解来简化SSH框架的配置,提高开发效率。以下是...

    SSH Framework 配置实例

    SSH框架,全称为Struts2、Spring和Hibernate的组合,是Java Web开发中常见的三大开源框架集成。这个框架集合提供了模型-视图-控制器(MVC)架构模式,以及依赖注入(DI)和面向切面编程(AOP)的能力,大大简化了Web...

Global site tag (gtag.js) - Google Analytics