J2EE 世界充斥着许多概念,如 MVC、Service、DAO 等等,而且这些许多都是“似是而非”的名词。如果不对这些概念进行厘清,恐怕不能全然贯通、明晰整个框架。这就体现了概念其重要性。鄙人希望:一、概念虽然会比较”笼统“,——这是因为概念处于较高层的位置,有涵盖的作用,但应该尽量明晰和收窄定义的范围;二、从概念到某件事物,是”抽象“到”具象“的过程,会形成某个特定”名词“,既然”名词“是特指的,那么应该没有二义性,即没有歧义;三、概念最后能找到真正所对应的具体事物,可以在代码中反映出来的。凡事须顾名思义,有其名就当有其实;四、理想情况下,一个系统不应存在重复的代码或模块。如果赋予一个事物有一个概念或名词 A,A 具有排他性,不能再使用,但可以包含其所属的 B 事物。即,A 是 A,B 是 B。如果 A 包含 B,那么 A ≠ B。
需要指出的是,本文所讨论的定义对其他框架不一定适用,请不要周延,当前仅限于 Bigfoot 界定概念所用——这也是为了避免争议的提前声明,同时这样也比较简单,点到即止。
基础概念
分层是应对复杂性的必然手段。解决一个个各自的小问题,最终来解决大问题,“分而治之”。
程序三层结构,毋庸置疑。

与数据库打交道的三个方法

以上脑图参考自《三层究竟如何?》。
我们比较推崇“半自动 SQL”的方法,既非直接写 SQL,也不是 ORM(也不熟悉 存储过程)。此方面的具体内容,参见前篇文章《浅浅地封装一层 JDBC》。
一般 SSH 结构

虽然有经典 SSH 架构摆在面前,但我们并不拘泥于此,同样积极寻找适应项目变化,可重用,方便程序员的架构方法,以适应于“互联网程序架构”而非单单“企业级开发”的架构。
JavaEE 体系架构
JavaEE 体系架构采用传统的 MVC 设计模式,分为 Model、View、Controller 三层,其中:Model 即模型层,定义数据模型和业务逻辑。
为了将数据访问与业务逻辑分离,提高业务精度,降低代码之间的耦合,模型层又细分为 DAO 层与 Service 业务层,DAO 全称为 Data Access Object(数据访问对象),将数据库访问代码封闭起来,MyBatis API 也在此封装,不再出现在其他层或向其他层暴露;业务层是整个系统最核心也最具价值的一层,该层对应用程序的业务逻辑进行封装,务求关注客户需求,在业务处理过程中会访问原始数据或产生新数据,或者需要持久化数据,DAO 层提供的 DAO 类能很好地帮助业务层完成数据处理,业务层本身则侧重于对客户需求的理解和业务规则的适应,自然也包括大部分的计算。
总体说来,DAO 不处理业务逻辑,只做与存取数据有关的获取原始数据或持久化数据等操作,为业务层提供辅助。
View 即视图层,为最终用户提供一个友好的交互界面,用户可以查看请求结果,也可以通过表单等交互手段实现数据录入。
Controller 层即控制器,他是 Model 与 View的桥梁,将二者很好的衔接,通过 View 接收用户数据,Controller 将数据传输给Model,Model 对数据进行处理;或者 Model 读取数据后,Controller 将数据传递给 View,View 向用户展示数据。一来一往,Controller 成了 Model 与 View 之间的快乐使者。具体来说,Controller 负责数据验证,跳转逻辑,赋值/取值。这里会出现 Request/Response 对象——但将 Request/Response 对象放到其他层就不合适了,举个反例,如果在 Service 层出现 Request/Response 对象那么有必要进行重构修正了。
- 从调用关系上看,左为高层,右为低层,下为高层,上为低层,高层可以调用低层,但低层不能调用高层,层与层之间的调用是单向的。
- 从数据传输上看,数据可以从视图层传输到 DAO 进而保存到数据库,也可以从数据库中读取数据进行处理或者显示,所以数据的传输是双向的。
- 从技术实现上看,视图层使用 HTML/JSP 组件实现,控制器使用 Servlet 或 Action 组件实现,模型层使用 JavaBean 组件或 EJB 技术实现。
MyBatis 封装在 DAO 层,负责数据访问操作;Spring MVC 充当控制器角色,对用户数据进行合法性检验和类型转换,为视图层提供标签简化页面显示,提供国际化支持等等;Spring是应用程序的管家,DAO、Service(业务)、Action 等对象由 Spring 创建并维护各对象之间关系,同时提供声明式事务管理,简化事务编程。
MVC 结构
可维护的结构

MVC 是个大领纲,不同的框架对 MVC 的理解和实践都不尽相同,有时候甚至很难一一对号入座,也有许多衍生分支的版本。实际上,MVC 是一种模式,模式不是公式,所以除了必要的条件限定之外,就没有一定的条条框框。

注意,前面说到的三层架构 ≠ MVC。
除了经典的 JSP + Bean + Servlet 架构外,还有一种更为简单的 JSP + Bean 模式。两者区别在于处理的主控部分不同。前者利用 Servlet 作为主控部分;后者利用 JSP 作为主控部分,将用户的请求、Bean 和事件响应有效的连接起来。
我比较倾向于 JSP + Bean 模式。该模式工作原理是:当 HTTP 发出请求时,JSP 接收请求并访问 Bean,当需要访问数据库或者服务器时,则通过 Bean 做相应处理。Bean 将处理的结果返回 JSP,JSP 生成动态 html 将结果传送到浏览器并显示,用户得到交互结果。JSP 作为视图,同时也有部分控制器的功能, Bean 组件作为模型和控制器组件。
这样的好处是首先不用配置 Servlet,一个 JSP 即是 Servlet(请求路径多起来维护麻烦),其次是 JSP 以显示为中心,它为 Web 前端开发提供了更方便的开发。再者是仍然可以做到显示和内容分离,显示和业务逻辑开发可以分开同时进行,实际上并不会在 JSP 嵌入大量 Java 代码,而且显得更简单。
必须提出的是,纯 service 接口的话应该使用 Bean + Servlet 的模式,那是完全排除表示层的考虑。

前端分层,围绕 HTML、CSS、JS 三大模块来处理。

模板技术我主张使用 Tag Files。有关 Tag Files 的更多教程,参见本博客《JSP Tag Files 技术总结》
项目选型
经过反复使用和衡量,决定采用下面的组件。分有前端和后端的(点击图片放大)。

前端堆栈我的方案如下:

Bigfoot 简介
“大脚 Bigfoot”是一款基于 JEE 平台研发的 Web 框架,继承了 Java 平台的高效、安全、稳定、跨平台等诸多优势,但却摒弃了传统 SSH 企业级架构所带来的庞大和臃肿,汲取了 PHP 的简洁和方便,非常适合互联网中小型网站的应用。
Bigfoot 是真正的开源、真正的免费,其代码直观、简洁,功能方面简单实用。该系统没有去做审核流程、复杂权限和日志等企业级的功能。对于这些功能,中小型互联网项目或许不是首要考虑;当前版本力求简单和通俗易懂的代码,也就是作为基础框架而考虑的。同时大脚也注重扩展性——更复杂的功能,可以留待二次开发。
个人认为 Java 在互联网应用之所以不及 PHP 那么受欢迎,其中一个主要是因为技术人员过分追求复杂的技术架构和为概念应用而应用(怕别人说技术不专业);搞得草根都不敢去用 Java 去了;于是这样的结果是严重阻碍了 Java 在互联网的应用和推广——而 PHP 在互联网的成功在于:简单和务实(PHP 的织梦成功正是这个原因)。我们的理念:Keep it simple, stupid,做一款简单实用的开源 Web。
迈普 CMS(MyPro Content Manage System)是以 Bigfoot 基础框架为依托的网站管理系统,具有懂 HTML 就能建站的优点,提供最便利、合理的使用方式。前端模板完全开放,可让用户随心所欲的自定义显示内容和显示方式。
“大脚 Bigfoot”整体特性:
- 免费、简单、开源
- 代码行数少,概念清晰,避免过渡设计
- 力求高聚合、低耦合
- 全栈式方案,包括前端控件和服务端接口,无缝对接
- 基于 JVM 的混合语言编程 = Java + JavaScript(服务端 Rhino)
- 基于 Java 1.7,积极采用 1.7 新语法,如 autoClose、多项异常抛出
- 特别适合从前端过来的开发人员,提供服务端 JS 环境
- 完备的文档、代码注释,并提供 JUnit 单元测试
- 谨慎采用第三方库,当前免 JAR 依赖(除数据库驱动外)。提倡“够用就好、就地取材”,例如,日志采用 JDK 自带的 Logger、JSON 转换让 JDK 自带的 Rhino 转换、数据连接池采用 Tomcat 自带的 Pool、页面标签就用 TagFiles/SimpleTag 甚至就是连 <%=……Java Code %> 都可以、使用 JSON 或 Map 作为通用的、交换的数据结构。
控制层特性:
- 回归简单性,结合灵活的 MVC 思想,强调前端、后端相分离,前端 = JavaBean + JSP;后端 JavaBean + Servlet。
- 基于 JSONTree 无序树的无限极分类,结构清晰,可维护性强。
- 提倡面向接口编程,实现了一个小型的 IOC 依赖注射和 AOP。
- 支持 JSON API 输出纯数据,与安卓、iOS、AJAX 甚至广义的客户端等等的无缝对接。
表示层特性:
- JSP 实际很强大完全可以做到类似 PHP 的页面优先的理念(互联网项目 UI 开发占了大量时间)——因此我们的数据绑定方式是 JSP 引入 JavaBean,而非 Servlet 控制。
- 有自己的前端库 Bigfoot.js(HTML5/CSS3/JavaScript),基于 LESS.js 预编译 CSS 器和 Node.js 的构建系统。
- 基于 JSP Tag Files 的模板继承机制,可复用性强。
- 模板语言基于 EL 表达式 和 SimpleTag。
- 后端管理界面可选使用 Ext JS。
数据层特性:
- 没有使用 ORM,提倡使用带变量的 SQL,提供简单的 SQL 封装手段和 DAO。
- 采用 Map 为输入、输出载体,如表单转换为 Map 再生成 SQL 语句;JDBC 返回数据采用 Map 为记录载体。
- 与 JS 引擎 Rhino 无缝对接,用 JS 定义一些方法,脚本语言 JS 在处理 JSON、字符串和正则表达式有极大的便利性和优势。
- 抛弃 Java POJO,改用 JS 定义实体,目的是为适合前后台所使用
- 使用 Tomcat 自带的数据库连接池 JDBC Pool。
最后,欢迎各位使用 Bigfoot!
Bigfoot 源码在
第一版,已废弃:http://code.taobao.org/p/bigfoot/src/
第二版,已废弃:http://code.taobao.org/p/bigfoot_v2/src/,请用 SVN 检出:http://code.taobao.org/svn/bigfoot_v2/。
第三版,基于 Spring + Spring MVC + MyBatis:http://code.taobao.org/p/bigfoot_v2/src/java_v3/。
那些年我看过的 Servlet + JSP 开源框架:
EWeb4J 1.9.1 新版本发布 基于Servlet/JDBC的轻量级web开发框架
PandaJS: Rhino + MongoDB + Server-side JQuery Template
HashMap关系数据映射技术
Eternal 框架
Nutz
Jfinal
Smart Framework
Tiny Framework
Firefly Framework
Solo
转:VO 与 PO 辨析
下面内容转载自 http://www.blogjava.net/lzhidj/archive/2011/02/21/344754.html
数据传输是程序员实现各种功能时刻需要考虑的问题,从数据模型的建立,到数据模型的转换,从数据的合法性验证,到数据类型的转化,我们要时刻小心,精心设计与组织。数据模型与数据传输可简单可复杂,完全取决于设计者的经验与意图,当然,项目的规模也是我们应该考虑的因素,一个小型项目实在没必要将问题复杂化。我们首先考虑数据从视图(View)传输到数据库(DB)的数据模型变化过程。用户从界面输入数据,此时,数据毫无结构可言,是零散的、无组织的,数据提交到控制器后,一方面考虑到 OOP 的严谨性,另一方面考虑到数据的封装,会将零散的无组织的数据封装成 Value Object(简称VO)对象,VO 就是 JavaBean,并传送到业务类中,在数据模型比较复杂的情况下,业务类的方法参数和返回值都应该是 VO 或 VO 的集合,VO 转换成 PO(Persistence Object)后传送到DAO,DAO 调用 Hibernate API 持久化数据。简单来说,业务类对外暴露的数据模型是 VO,DAO 对外暴露的数据模型是 PO。
数据从数据库传递到视图层的数据模型变化恰恰相反,Hibernate将数据库中的记录转换成 PO,PO 传递给业务类后转换成 VO,VO 被传送到视图层进行显示处理。以上转换过程如下图:

为什么同时需要 PO 和 VO?前面说过,这不是必须的,如果项目比较小,直接使用 PO 就行了。但 PO 有如下缺点:PO 反应了数据库的物理模型,向外暴露 PO 存在一定的风险;
- PO过于僵化,无法适应变化莫测的业务需求;
- PO在持久化状态下与数据库同步,可能导致数据意外修改;
- PO将导致程序缺乏健壮性。
而VO没有PO的缺点,相对而言,VO更加灵活,能适应各种需求的变化,下面是 PO 和 VO 的区别:
- VO 是用 new 关键字创建,由 GC 回收的。PO 则是向数据库中添加新数据时创建,删除数据库中数据时削除的。并且它只能存活在一个数据库连接中,断开连接即被销毁;
- VO 是值对象,精确点讲它是业务对象,是存活在业务层的,是业务逻辑使用的,它存活的目的就是为数据提供一个生存的地方。PO 则是有状态的,每个属性代表其当前的状态。它是物理数据的对象表示。使用它,可以使我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换;
- VO 的属性是根据当前业务的不同而不同的,也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。PO 的属性是跟数据库表的字段一一对应的;
- PO 一般只有一个,但对应的 VO 可能有多个。
我们举一个简单的例子来说明 VO 与 PO 的区别:比如要实现用户注册与登陆的功能,在物理模型中创建一个用户表,字段分别为用户 ID(标识列)、用户名、密码、注册日期等等,定义 PO 时应该有这四个字段的映射属性。现在,我们来实现用户注册这一功能,为了接收用户输入的注册信息,必须定义 VO,注册用户需要输入的信息有:用户名、密码1、密码2,这正好是 VO 的属性。而实现用户登陆功能时,用户需要输入的信息只有用户名和密码,所以,该 VO 的属性只有两个:用户名、密码。可以看出,PO 侧重于物理模型,而 VO 则更关注用户的实际需求。如下图所示:
