JEMSF框架
前言
今天我们准备向广大开发人员推荐一种新的框架,暂时取名JEMSF,如果您已经对Struts、Tapestry以及Spring和Hibernat有一些了解,那么应该可以更好的理解下面的文章,JEMSF是我在工作生涯中最大的一个创造,经历了很多考验和应用的试验,最后形成JEMSF。
序
一种新的事物的诞生需要经历很多的考验,我自认为JEMSF是一个很好的WEB应用框架,很久以前(2002年)JEMSF就已经出了第一个版本,定义为JEMSF Beta 1.0.0,回忆那一段美好的过程,其实是开创JEMSF艰苦的过程,我心情特别激动。我从2001年开始撰写JEMSF的基础库文件,通过和国外的一些工程师和一些学校的老师进行充分和长期的沟通,我得到了很多我以前从来没有的经验和知识;通过对Struts的研究,我领会到Struts的精神所在;通过日以继夜的撰写和修正,使我懂得新生物的创造过程的艰辛;通过不断的试验我得到了一种肯定,如果我放弃了JEMSF,那么我觉得我的生命可能会很没有意义。
今天,你看到的是JEMSF3.0的版本,已经在超过10个大中型项目进行实施。不凡有:
http://xiamen.airchina.com.cn(中国国航厦门营业部)
http://www1.widetrust.com.cn/AirAgent/Login.jsp
(航空服务系统)
http://www.vspd.cn:8081(TelAgent呼叫管理系统)
http://www.vspd.cn:83(PID Manager预定系统)
http://www.2617788.com(厦门大华假日同行网)
等电子商务网站和应用系统等这些大型电子商务应用网站。他们有的是在线预定系统,有的是实时销售系统,有的是大型运价文件发布系统。还有一些没有列入的系统,比如不能公开的航空公司的内部应用系统。这些系统每天要处理的数据量超过10000条,特别是前面几个,每天的订单量超过20000个。这些似乎不是我们要关心的数据,但是这些是证明了JEMSF诞生的必要性,是我准备向外界发布JEMSF的基础,没有得到很好的实验和考验之前我似乎没有什么底气拿出JEMSF,而现在,我可以大胆的拿出去了,和所有的开发人员共享我的创造。
1、JEMSF框架简介
JEMSF是一个J2EE应用开发基础框架,类似流行的Struts、Spring等实用框架。如果您已经对Struts框架有一些了解,那么开始JEMSF框架的学习将会变得更加容易。JEMSF简化了J2EE应用开发过程,为J2EE开发人员提供了基础组件,以便J2EE开发人员充分的发挥他们在业务逻辑上的才能,缩短基础组件的开发周期,从而缩短项目的开发周期。
JEMSF跨越MVC的3个层次(界面层V、控制层C、模型层M),不但提供基础的数据源的访问和界面简易标签,也提供控制逻辑的控制器,同时提供数据流与控制流分开控制,极大的方便开发人员进行应用系统的开发和设计。
JEMSF的基础组成部分:中央控制器组件,实现请求事件的综合控制,利用XML技术,采用配置文件方式实现控制请求;数据传输组件,实现层次间(比如MVC的控制层和模型层、表示层与控制层)或是对象间(比如一个控制类和一个显示页面)数据的有效传输和管理,在Web应用中可以结合Session和Application实现数据的有效转移,在普通应用中可以实现数据合理组织和分发;错误处理机制,有效的管理错误分类,实现错误描述统一性和程序的规范性;数据库操作组件,实现数据库操作的封装和保护,结合XML文件,摆脱了SQL语句和程序的紧耦合,实现数据库连接池的自动化管理,对于开发人员而言,数据库的操作变得极其的简单,代码可复用性极大的提高。数据库结果集操作也得到优化,对数据库的传入和传出参数有更好的对象给予支持;界面简易标签组件,把页面的基本对象全部封装成简易的对象,方便控制每个基本组件的特性,也支持验证过程;简单工具集合,提供对字符串、日期的操作,后期在不断的扩展中可以不断的增加工具内容,支持不断的扩张。
以下内容简要讨论Struts架构和Tapestry以及JEMSF框架对比。我们将看到这三个框架是如何清晰地区分控制,事务逻辑和外观,从而简化了开发应用程序过程的。我们还将介绍这三个框架提供的类如何使得开发工作更加简单,这些类包括:
控制程序流程的类
实现和执行程序事务逻辑的类
自定义的标记库使得创建和验证HTML表单更加容易
Struts框架具有组件的模块化,灵活性和重用性的优点,同时简化了基于MVC的web应用程序的开发。
JEMSF也是一个J2EE应用开发基础框架,类似流行的Struts、Tapestry等实用框架,并加入了数据库处理模块,是开发人员不必在Struts基础上结合Spring和Hihernat,JEMSF已经基本具备了他们结合的功能了。
Tapestry使用了组件库概念替代了标签库,没有标签库概念,这样就没有标签库和自己的组件需要结合的问题,都是组件的使用,组件中分Tapestry标准组件和自己定义的组件,这也是接触了Jsp体系的人学习Tapestry面临的一个思路转换。
从目的上看,JEMSF和Struts以及Tapestry3.0都是共同的目标:简化了J2EE应用开发过程,为J2EE开发人员提供了基础组件,以便J2EE开发人员充分的发挥他们在业务逻辑上的才能,缩短基础组件的开发周期,从而缩短项目的开发周期。
比较项目 Strust Tapestry3 Jemsf
在View显示的组件要求 组件必须继承ActionForm 分显式调用和隐式调用,组件必须继承BaseComponent 有一套基于HTML的包装的库文件
组件在View显示粒度 View页面只能显示与表单对应的ActionForm,配置中Action ActionForm 页面一般只能1:1:1关系。 可将组件嵌入页面任何一行,对使用组件数量无限制。 类似Tapestry3
页面分区tiles 使用Tiles标签库实现,需要另外tiles-def.xml配置文件 组件有自己的视图页面,通过调用组件即直接实现多个页面组合。强大自然的页面组合是其特点。 类似Tapestry3
页面跳转 使用标签库html:link中写明目标URL,URL名称需要对照配置文件的path命名,与组件Action耦合。 URL名称是目标的组件名称,不涉及URL和路径等操作,方便稳固 类似Tapestry3
参数传递 使用html:link时传递参数超过一个以上处理麻烦。 直接调用组件,直接赋予参数,没有参数个数限制 由一组数据组件组成:自定义HDTO
HDTO的组成由一组对象组成,层次关系如下图,对象之间是无限扩展,比如一个BsFormData可以包含多个BsFormTable,一个BsFormTable可以包含多个BsFormRecord,一个BsFormRecord可以包含多个BsFormField。
事件触发 通过表单提交submit激活,不能细化到表单里字段。 能够给于表单每个字段贴一个事件,事件组件必须实现PageListener接口 每个HTML可以执行的事件都可以被包装,结合JS实现任意可以执行的事件
数据库操作 一般结合Spring和Hibernat 没有提供 提供一套结合不同数据库的组件,主要由:
BsSQLStatementIDS -SQL语句处理器
BsSQLConditionIDS -SQL语句条件处理器
BsSqlXmlPaser -SQL语句解释器
BsSQLHandler -SQL执行器
DataBaseConfig.xml -SQL语句存放配置文件
数据池管理 一般结合Spring和Hibernat 没有提供 提供了一组对象实现连接池管理:
BsDataBaseConection –连接池管理对象
DataSourceConfig.properties -连接池配置文件
2、JEMSF组成部分
2.1、控制器(BsServletController):接收JSP或是HTML提交的事件,识别事件编号,把“事件编号-EventID”提交给控制器的XML解释器,控制器的XML解释器(BsControllerXmlPaser)读取控制XML文件,帮助控制器确定应该把事件交给哪个具体的处理EdgeBean(业务边沿Bean――EB:在框架中,EB是一个很重的对象,可以通过EB实现业务逻辑和中间的通道)进行业务处理,所有的控制过程都有控制器实现转移,包括错误处理页面转移。
控制器XML配置文件(TransConfig.xml)的格式:
<?xml version="1.0" encoding="GB2312"?>
<JemLee>
<page id="LoginPage"><!--类型1:带多个状态的配置对-->
<form id="LoginForm">
<jsp jsppage="/LoginRs.jsp" />
<transitions>
<transition event="LoginEventId" process=".servlet.LoginEdge">
<states>
<state value="LoginEventIdReturnValue">
<goto page-id="LoginPage" form-id="LoginForm" />
</state>
<state value="100">
<goto page-id="ERRORPAGE" form-id="ERRORFORM" />
</state>
</states>
</transition>
</transitions>
</form>
</page>
<page id="AgentTypePage"><!--类型2:没有状态的配置对-->
<form id="AgentTypeForm">
<jsp jsppage="/agenttypes.jsp" />
<states></states>
</form>
</page>
<page id="ERRORPAGE"><!--类型3:错误配置对-->
<form id="ERRORFORM">
<jsp jsppage="/error.jsp" />
<states></states>
</form>
</page>
</JemLee>
2.2、Error错误处理器:接收多个对象的访问,通过错误ID识别错误内容,并把错误内容传递给调用对象,时限了错误或是提示的有效的管理和归类。
2.3、数据传输对象(DTO):作为层次间(比如MVC模式的控制层和模型层、表示层与控制层)、对象间(比如一个控制类和一个显示页面)数据的交换对象,在Web应用中可以结合Session或是Application实现数据的有效转移,在普通应用中可以实现数据合理组织和分发。通过HashTable实现数据的灵活控制。DTO的组成由一组对象组成,层次关系如下图,对象之间是无限扩展,比如一个BsFormData可以包含多个BsFormTable,一个BsFormTable可以包含多个BsFormRecord,一个BsFormRecord可以包含多个BsFormField。
2.4、SQL处理器:分析EgdeBean(EB)或是其他对象提交的SQL处理,通过SQL编号分析数据库处理的XML文件,构建SQL语句对象,SQL语句的XML配置文件(DataBaseConfig.xml)格式如下:
<?xml version="1.0" encoding="GB2312"?>
<DataAccessSQLStatement>
<sql id="SQLStatementIds" table="OpertionTable"> <!--类型1:SQL语句基本结构-->
<!--基本SQL语句-->
<query>select * from caxmagentuser </query>
<!--SQL语句的条件部分-->
<condition> where '1' = ??</condition>
<!--条件中的参数类型-->
<datatype>
<dataelement>String</dataelement>
</datatype>
<!--希望返回的字段域-->
<datatype>
<dataelement>IDS</dataelement>
<dataelement>AGENTUSER</dataelement>
<dataelement>AGENTPWD</dataelement>
<dataelement>IATANUM</dataelement>
<dataelement>comname</dataelement>
</datatype>
</sql>
<sql id="search1"> <!--类型2:SQL语句条件结构-->
<!--附加的条件-->
<condition> and startplace = ?? and endplace = ??</condition>
<!--条件中的参数类型-->
<datatype>
<dataelement>String</dataelement>
<dataelement>String</dataelement>
</datatype>
</sql>
<sql id="SQL00001" table="InsurCompany"> <!--类型1:SQL语句基本结构-->
<!--基本SQL语句-->
<query>select * from caxmagentuser </query>
<!--SQL语句的条件部分-->
<condition> where '1' = ??</condition>
<!--条件中的参数类型-->
<datatype>
<dataelement>String</dataelement>
</datatype>
<!--希望返回的字段域-->
<datatype>
<dataelement>InsurCompanyID</dataelement>
<dataelement>Company_user</dataelement>
<dataelement>Company_passwd</dataelement>
<dataelement>InsurCompanyName</dataelement>
<dataelement>InserCompanyFullName</dataelement>
</datatype>
</sql>
</DataAccessSQLStatement>
2.5、连接池管理对象:支持SQL处理器和数据处理对象对数据库连接的需要,自动管理从数据库获取需要的数据库对象,并在使用完之后自动关闭数据库连接对象,把连接还给应用程序服务器的数据库连接池。
2.6、数据处理对象:实现数据的具体操作,可以高效的实现数据访问,支持多种数据库,比如SQLServer、Oracle、Sybase、MySQL、DB2等等。对部分数据库支持多结果集合操作,比如SQLServer,具体的支持情况和数据库不同略有不同。
3、JEMSF框架逻辑
上图按照MVC模式设计把JEMSF的逻辑表述出来,绿色部分为开发商需要实现的代码(下文会提供参考代码),比如,JSP页面可复用访问脚本,EdgeBean可复用代码等。其他对象都属于JEMSF的组成部分。JEMSF包括了整个MVC模式的主要组成部分,层次明了,易懂,参考实现容易。
4、JEMSF开发环境建设
JEMSF适合多种J2EE应用的开发环境,使用JEMSF作为J2EE应用的基础框架进行应用程序的开发与选择的开发环境没有多大影响,比如选择下列组合都是适用:
(1)MyEclipse+Eclipse+Tomcat+MySQL+Macromedia Dreamweaver
(2)Jbuilder+ Tomcat+MySQL+Macromedia Dreamweaver
(3)WSAD+WAS+DB2 +Macromedia Dreamweaver
还有更多的组合无法一一列举。下文采用(1)方式加以说明,其他开发环境大同小异。MyEclipse、Eclipse、Tomcat、MySQL、Macromedia Dreamweaver各自安装完毕。
(1)在MyEclipse环境下创建一个Web项目-MyBookStore
○1新建视图
○2创建成功之后视图
(2)把JEMSF的java代码(com.下面的内容)拷贝到MyBookStore的MyBookStore\src目录;把jdom.jar(0.8版本)拷贝到MyBookStore\
WebRoot\WEB-INF\lib目录;再把config目录拷贝到MyBookStore\
WebRoot下面。
○3添加JEMSF相关内容之后视图
(3)把控制器Servlet的控制增加到web.xml。打开web.xml,添加如下代码:
○4添加控制器的控制之后视图
上述内容是对JEMSF开发环境的配置过程,到现在位置,已经完成JEMSF的开发环境的配置,接下来的就可以进入开发工作了。
5、JEMSF之Login-Hello例子
我们通过一个简单的登陆画面,了解JEMSF的运作基本原理。完成这个实例需要5个步骤,下面逐一给予说明:
(1)创建一个JSP文件,命名Login.jsp,添加必要的内容,参考下文:
<%@ page contentType="text/html; charset=gb2312" language="java" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Login-Hello</title>
<script language="javascript">
//登陆方法的脚本调用
function Login(){
if(document.form1.username.value == ""){
alert("请输入用户名!");
return;
}
if(document.form1.passwd.value == ""){
alert("请输入密码!");
return;
}
//注意do=login
var url = "servlet/controller?EventID=LoginEventId&do=login";
document.form1.action = url;
document.form1.submit();
}
</script>
</head>
<body>
<form name="form1" method="post">
<table width="30%" border="0" cellpadding="1" cellspacing="1" bgcolor="#FFCCFF">
<tr bgcolor="#99CCCC">
<td width="33%">用户名称:</td>
<td width="67%">
<input type="text" name="username">
</td>
</tr>
<tr bgcolor="#99CCCC">
<td>用户密码:</td>
<td>
<input type="password" name="passwd">
</td>
</tr>
<tr bgcolor="#99CCCC">
<td colspan="2">
<input type="button" name="btnLogin" value="登陆" onClick="Login()">
<input type="reset" name="btnReset" value="重置">
</td>
</tr>
</table>
</form>
</body>
</html>
运行效果如图:
(2)创建一个Servlet文件,命名LoginEdge.java,并添加一些内容,具体内容如下:
Web.xml增加如下内容:
LoginEdge.java的内容如下:
package com.mybookstore.presatation;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginEdge extends HttpServlet {
/**
* 序列号
*/
private static final long serialVersionUID = 8186644736195297412L;
/**
* 接受Get请求
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 归并到同一方法
this.doPostGet(request, response);
}
/**
* 接受Post请求
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 归并到同一方法
this.doPostGet(request, response);
}
/**
* 接受来自Post和Get所有请求
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void doPostGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
//获取目标事件
String tg = request.getParameter("do");
//转向目标事件
if (tg.equals("login")) {
login(request, response);
}
}
/**
* 登陆初始化
* @param request
* @param response
* @throws ServletException
* @throws IOException
* @throws FileNotFoundException
*/
private void login(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, FileNotFoundException{
HttpSession session = session = request.getSession();
try {
//从界面上获得用户名成和密码
String user = request.getParameter("username");
String passwd = request.getParameter("passwd");
System.out.println("打印在控制台上:"+user + " -- " + passwd);
//把用户名称和密码放置在Session
session.setAttribute("username",user);
session.setAttribute("passwd",passwd);
//告诉jemsf该到那里去找对应的页面
session.setAttribute("valueNo", "LoginEventIdValue");
} catch (RuntimeException e) {
//错误处理页面
session.setAttribute("valueNo", "100");
session.setAttribute("MsgBoxInfo",e.getMessage());
}finally{
//把控制权还给控制器
RequestDispatcher rd = request.getRequestDispatcher("/servlet/controller?EventID=LoginEventId");
rd.forward(request, response);
}
}
}
(3)在TransConfig.xml增加如下配置现:
(3)创建LoginRs.jsp增加如下内容:
<%@ page contentType="text/html; charset=gb2312" language="java" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title></title>
</head>
<body>
<%
String user = (String)session.getAttribute("username");
String passwd = (String)session.getAttribute("passwd");
out.println("Helo :"+user);
%>
</body>
</html>
(4)创建error.jsp增加如下内容:
<meta http-equiv="Content-Type" content="text/html; charset=gbk">
<title></title>
<style type="text/css">
.style1 {
color: #FFFFFF;
font-weight: bold;
}
</style>
</head>
<body>
<%
session.setAttribute("valueNo","");
session.setAttribute("CFlag","0");
%>
<p> </p>
<table width="49%" border="0" align="center" cellpadding="1" cellspacing="1" bgcolor="#96BAAE">
<tr>
<td height="19" align="center" bgcolor="#006666" ><span class="style1">系统提示</span></td>
</tr>
<tr>
<td height="80" bgcolor="#FFFFFF" align="center">
<%=(String)session.getAttribute("MsgBoxInfo")%>
</td>
</tr>
</table>
</body>
</html>
(5)发布MyBookStore项目并运行,在浏览器输入:
http://localhost/MyBookStore/Login.jsp
界面显示:
在用户名处输入Test,密码随意输入然后点击登陆,界面显示:
如果系统在运行过程中出现业务逻辑问题,那么系统会自动把错误信息交给error.jsp显示出来。
上述5个步骤已经完成了JEMSF一个简单的例子,例子中出现了很多新的概念和名次词,我们会在后文中给予描述和体现。
6、JEMSF组件间关系
我们回顾下图:
控制器起着一个枢纽作用,所有的请求都通过控制器,再传递给其他处理的EdgeBean。配置文件的解释器作为辅助作用。转向配置文件(TransConfig.xml)具体描述了处理流程。
在MVC的C(控制层)中,主要的组件就是控制器、TransConfig.xml的解释器、错误处理器、错误描述文件(ErrorMsg.properties)、数据流组件(BsFormData)以及EdgeBean。
在MVC的V(视图层)中,主要的组件有htmlwriter(界面主要控件的包装,比如TextBox)、数据流组件(BsFormData)、错误处理器。
在MVC的M(模型层)中,主要的组件有数据库处理器(BsSQLHandler)、数据库语句描述文件(DataBaseConfig.xml)、DataBaseConfig.xml 的解释器、数据流组件(BsFormData)、错误处理器。
V到C主要通过控制器,C到M主要是通过EdgeBean。
7、JEMSF应用开发细节
前文已经对JEMSF做了简要介绍,并给出了简单的例子,如果你已经对仔细的阅读上文,那么您已经具备使用JEMSF的基本能力。至少从V-C已经掌握了,接下来我们对上文的例子进行深化。
7.1、界面要素说明
对于JEMSF,界面要素主要关注对象的Click事件,当一个对象的Click事件发生时,用户可能希望看到一些效果,比如保存用户填写的数据,所以在对象执行Click事件之后,那么就可以抓住该事件进行处理,比如下文:
<input type="button" name="btnLogin" value="登陆" onClick="Login()">
这是一个按钮对象,按钮对象有一个OnClick事件,当按钮按下时执行Login()函数,这是一个JavaScript的函数。我们需要关注的就是在这个JavaScript函数中作什么动作。
<script language="javascript">
//登陆方法的脚本调用
function Login(){
if(document.form1.username.value == ""){
alert("请输入用户名!");
return;
}
if(document.form1.passwd.value == ""){
alert("请输入密码!");
return;
}
var url = "servlet/controller?EventID=LoginEventId&do=login";
document.form1.action = url;
document.form1.submit();
}
</script>
红色部分代表该页面向控制器发出动作请求,要求控制器执行事件ID为LoginEventId的内容,其中执行的分支叫做login。
【备注:以下的内容在下一篇文章中叙述,大概在1个月内可以完成】
7.2、控制器原理
7.3、TransConfig.xml要素说明
7.4、错误处理原理
7.5、ErrorMsg.properties要素说明
7.6、数据库处理原理
7.7、DataBaseConfig.xml要素说明
7.8、EdgeBean编程例子
7.9、BsFormData编程例子
7.10、htmlwriter编程例子
7.11、JEMSF在应用服务器的部署
8、JEMSF在中大型应用设计中提供帮助
9、个性化应用例子
10、结束语
附加:如果您感兴趣使用JEMSF,请联系[email protected],获取源代码。
“本文作者李克喜 是 www.vspd.cn 的架构师。他目前在中国厦门携航电子商务有限公司工作,从事JAVA EE的研究和JAVA EE应用的开发。可以通过 [email protected] 与他联系。”