一种基于SSH的快速开发框架

概述
这并不是一个具体的项目,而是在开发具体项目过程中,逐渐系统归纳并渐成体系的一套快速的开发框架。其充分应用了Spring的依赖倒转特性,结合前端显示的开源框架,可以快速的开发对数据库表的增删改查功能,使程序员从繁琐的SQL和大量重复的代码中解脱出来,专注于业务逻辑的实现。这个项目也是实现OO设计的一个典范。

SSH指Hibernbert+Spring+Webwork。这里用Webwork替换了Struts,其实对于展示层来说,用何种开源框架都是类似的。且Webwork比Struts更加规范,Webwork实际上也是Struts2的一种平滑过渡。所以这里依然命名为基于SSH的快速开发框架。

这个框架的初衷源于做SSH项目时的两个问题:

  1. 作为一个重要的功能,对数据库的查询,如何能做到快速和标准化的实现?我们知道程序员需要善于写SQL来进行查询,但是在Java的持久化层,大量复杂的SQL调用是否合理,是值得商榷的。Hibernate作为最广泛使用的开源框架,其HQL足够强大,而我个人认为,Criteria的设计更加契合Java的面向对象开发理念。如果任由程序员使用HQL构造系统,则持久化层要写很多重复代码,且无法做到很好的对象化,容易失控。

  2. 中间层(业务层)的作用体现在哪里?从纯JSP的最初开发阶段,我们习惯于将业务逻辑写在page上,极其头痛的维护迫使人们考虑分离显示层,利用Action(Struts和Webwork等)的方式将逻辑挪到Java的层面上,使得page上只有那些标签。而我们知道在Action中可以直接构造查询条件,并调用持久层以获取结果,那么如何体现以Spring为代表的中间层的作用呢?

出于解决这两个问题的目标,这个框架实现了如下的特点:

  1. 封装查询条件。为了解决第一个问题,该框架在持久化层做了一些对象化的工作。其前提是尽量使用Criteria的设计。所谓封装,即将每一个查询条件设计为一个对象,其中包含了查询条件的值,以及需要的关系(大于,小于,为空等等);
  2. 充分利用Spring的依赖倒转。为了解决第二个问题,在Action的方法体中,通过调用各种Manager类来实现业务逻辑,而Manager全部使用Spring的依赖倒转在运行时进行注入,由容器控制。这样做的好处是真正实现了三个层次的分离,各司其职,同时便于扩展。该框架设计的比较早,对于Spring的依赖注入还使用的是配置文件的方式,工作量也是比较大的。最近研究JSF的实现,发现使用Annotation是一个潮流,相信Spring的新版本也会在这方面有些改进;
  3. 模板方法模式的应用。在显示层和业务层,核心处都使用了模板方法模式,即由抽象类实现算法步骤,需要由具体类具体行为的地方,用抽象方法代替。这样具体类就简化为实现这些具体行为(抽象方法),具体类看起来更加像一个”对象“,而非一种”过程“;
  4. 符合OO设计的原则。基于接口编程,而非基于具体实现编程,是OO设计的基本原则。该框架的设计完全符合这些OO的原则。在三个层次上,接口,抽象类,具体类形成了完美的交互关系,相互支撑,最大限度的减少了冗余代码,也便于扩展。

由于有了以上的特点,利用这个框架开发比较简单的SSH应用,有如下的优点:

  1. 标准化。该框架很好的封装了持久化层,规范了显示层和业务层。开发工作趋于标准化,程序员只需要集中在业务逻辑的实现,不需要关心底层的实现,也不想写很多重复的代码;
  2. 快速开发。由于有了标准化的优点,对于新功能的加入,可以做到快速的实现。特别是针对那些简单的功能,甚至不需要写代码,只需要在Spring的配置文件中添加配置语句即可;
  3. 易于扩展。优秀的OO设计目标就是易于扩展,不论是持久化层更新框架,还是对原有业务逻辑进行升级,在该框架的支持下,都可以快速的实现。

持久化层
持久化层对条件的封装,是这个框架的核心变化,所以最先进行描述。我们考虑这样一个场景,对某个表进行综合查询,并把结果返回给用户。这个表包含了各种类型的字段,行数也很多,我们的查询也许会比较复杂,程序员会很自然的写出"select ... from XXX where ... and ... order by ..."。针对这么一个基本需求,我们会首先考虑对where子句的内容进行改造和封装。这就是PojoValue接口,PojoValueSupport抽象类以及具体的PojoValue类的工作。

这种封装的机制是这样的:任何一个where条件都有一个字段名称(variable),点值或极值(current, min, max)以及他们之间的关系(大于,小于,等于,为空等等)。那么一个具体的PojoValue类会存储这些信息,相当于把一个where条件进行了对象化。其中,PV接口可以返回这个条件封装类的查询条件(HQL形式或Criterion形式),而PVS抽象类包含了字段名称,和一些boolean值的关系,因为这两个都是不随具体类而变化的。继承PVS的具体类,则包含了不同对象的点值和极值,例如IntegerPojoValue包含了Integer类型的current, max and min;DatePojoValue包含了Date类型的current, max and min。为了方便构造,我们会将这些具体的PojoValue类设计成单例模式,后面会看到调用的实例。

其次,考虑对整条查询语句进行对象化,我们就得到PojoWrapper接口和PojoWrapperSupport抽象类。前者定义了获取整个查询语句的方法(依然是HQL形式或DetachedCriteria形式),后者包含了必要的一些元素,例如表名(tableName),排序关系(orders)和映射字段(projections)。对于实现该抽象类的具体类,应该根据要实现的查询条件,置入一定的PojoValue具体类。例如一个对整型的判断,就需要一个IntegerPojoValue,一个对日期型的判断,就需要一个DatePojoValue。

至此,整个查询语句就对象化完成了,这个对象是一个实现了PojoWrapper的具体类,其功能就是返回DetachedCriteria对象或者代表HQL的字符串对象。最后,我们需要DAO类来获取真正的数据,这是DaoSupportWrapper接口的工作。它通过一个方法接收DetachedCriteria对象(或String对象,代表HQL),返回结果集(List)。至于具体的实现,我们使用Hibernate来做,所以自然的以一个HibernateDaoSupportWrapper来定义,并且由于该类继承了HibernateDaoSupport,后者为Spring提供的对Hibernate的支持,所以具体实现非常的简单。

业务层
持久化层结束于Dao的设计,对其调用,根据业界的常规做法,是业务层提供的Manager接口的工作。ManagerSupportWrapper接口是极其重要的,它连接了持久化层。一方面,由于其实现类会内置一个对应的DAO类,所以连接了持久化层,并获得查询结果;另一方面,它通过createWrapper这个重要的方法,将显示层接收到的用户输入,以FormBean的方式整体输入,并在这里转化为PojoWrapper(持久化层对查询条件的封装)。

WrapperBehavior是一个抽象类,部分实现了ManagerSupportWrapper接口。它通过内置的Dao去获取查询结果,而createWrapper方法则使用模板方法模式给出最基本的算法步骤,只在添加真正的PojoValue和其它条件的地方,留给具体类实现。

显示层
显示层最重要的是各种Action的设计。这里两个问题,一个是各种Action的继承或实现关系,二是对Manager类的注入和使用。就Webwork来说,所有的Action应该都继承自ActionSupport类,我们定义了一个Action类,并继承自Webwork的系统类,以实现和页面的通信。此外Action类还支持异常出现后的处理,特别是发送到页面的提示信息,这部分基本也对象化了。ModelAction是Action的一级子类,其实现Webwork的ModelDriven接口,以接受页面信息。以QueryAction为典型的二级子类,过载execute方法,实现各自的业务逻辑和转向功能。

第二个问题是通过Spring来实现的。QueryAction中会有一个ManagerSupportWrapper对象,Spring会在运行期根据配置注入不同的具体Manager。而QueryAction会在其execute方法中,调用Manager的createWrapper,将FormBean对象转换成Wrapper对象,并再次使用Manager进行实际结果的调用。

显示层还会处理结果显示,异常提示等和页面更紧密的通用功能,对这部分也尽量使用OO的思想予以对象化的设计。由于显示层和Webwork联系较为紧密,这里就不再赘述了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,717评论 6 342
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,559评论 18 399
  • (一)Struts、Spring、Hibernate、Mybatis框技术 1.Struts2.0有几种标签库 【...
    独云阅读 3,218评论 0 62
  • 八月,我的眼睛是两只杯子 杯中有美丽的诗歌和 真正的诗人,海子也在杯中 母亲送我的这对美丽的杯子 由空到满,由婴儿...
    向史而新阅读 195评论 4 9