EasyJWeb是基于java技术,应用于WEB应用程序快速开发的MVC框架,框架设计构思来源于国内众多项目实践,充分借签了当前主流的开源Web框架(Struts、JSF、Tapestry 、Webwork),吸取了其优点及精华,利用Velocity作为模板页面引擎,是一个实现了页面及代码完全分离的MVC开发框架,是一个旨在为中小型Web应用系统提供快速开发实践的简易Web框架。
(为了能让用户快速地掌握并使用EasyJweb框架,在开发EasyJWeb的同时开发了EasyJWeb Tools。)通过EasyJWeb Tools提供的配套开发工具,可以实现基于EasyJWeb框架的Web应用项目快速开发,包括常用应用软件的代码自动生成、数据库添删改查(CRUD)代码生成、自动页面模版生成、配置文件管理等。
框架特点:
1、零配置文件支持。以前我们每接触一个框架,开始总是被他的配置文件折腾一番。EasyJWeb实现零配置支持,可以不写一句配置文件就在框架基础上构建你的应用。(适合小型网站系统)。
2、简易的模板页面设计支持。放弃使用jsp,使用简单Velocity脚本语言作为页面模板。
3、页面程序完全分离。实现显示页面与程序逻辑的完全分离,克服了传统jsp页面难于维护的问题。
4、基于页面组件的框架。灵活的页面组件配置,可以直接通过配置文件设置字段及表属性的事件等。
5、快速开发支持。通过EasyJWeb Tools,可以自动生成应用软件模板代码,定制并管理自己的J2EE代码模板,代码生成模板扩展支持最流行的实用Java开发框架(如hibernate、Spring等),实现快速开发。

EasyJWeb的MVC框架简单流程图
二、环境需求
三、安装配置EasyJWeb
获取EasyJWeb SDK
EasyJWeb是一个开源项目,你可以从EasyJF官方网站下载。下载地址:http://www.easyjf.com/easyjweb/download.htm。其中,EasyJWebX_src.zip为源代码,EasyJWebX_lib.zip为编译后的jar包及运行EasyJWeb所需要的基本包。
安装
EasyJWeb只是一个Web应用程序MVC框架,其主要作为一般应用程序的基础框架,是以一个jar文件的形式伴随在用户的Java Web程序中执行,不需要进行专门的安装操作。只需要把easyjweb.jar文件及附属文件(lib中的所有jar文件)拷到java web应用程序类路径中即可。我们一般是放到Web应用程序目录的Web-inf\lib目录下,也可以拷到Tomcat\Common\lib目录下,这样使得所有Tomcat Web应用程序都可以使用EasyJWeb。我们推荐放到web-inf\lib 目录下,这样可以使得你不用理会具体的Web应用服务器.
当开发EasyJWeb应用的时候,只要把EasyJWeb.jar包导入到工程中即可执行应用程序的调试及测试等工作。
配置web.xml文件
EasyJWeb是一个Web应用程序的核心骨架,为了能在浏览器中访问EasyJWeb应用程序,需要配置应用程序中的web.xml文件,使得用户的Web应用程序知道何时使用EasyJWeb应用程序处理用户请求。
只有通过配置Web应用程序中的web.xml文件,才能使得EasyJWeb能正常的使用。EasyJWeb应用程序默认以.ejf为扩展名,在用户地址栏中输入与.ejf结尾 URL路径的时候,为了使得EasyJWeb能处理用户的请求,需要在web.xml文件中加如下的配置信息:
<servlet>
<servlet-name>easyjfservlet-name>
<servlet-class>com.easyjf.web.ActionServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>easyjfservlet-name>
<url-pattern>*.ejfurl-pattern>
servlet-mapping>
由于Java是外国人发明的,其以unicode为默认编码,并且很多Java Web应用服务器也是泊来之品。因此我们在进行Java Web应用开发的时候经常会遇到中文字符处理的问题。比如显示在页码上的中文信息成乱码、或者保存到数据库的变成乱码等。因此,为了让Java Web应用能更好的处理中文,EasyJWeb默认情况下使用utf-8作为标准编码。这样需要通过在web.xml文件加入下面的设置,使得转码程序能正确运行,显示正确的文字字符。
<filter>
<filter-name>CharsetFilterfilter-name>
<filter-class>com.easyjf.web.CharsetFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>ignoreparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharsetFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
这样,一个基于EasyJWeb的Java Web应用程序的web.xml文件全部内容如下:
xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>easyjfservlet-name>
<servlet-class>com.easyjf.web.ActionServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>easyjfservlet-name>
<url-pattern>*.ejfurl-pattern>
servlet-mapping>
<servlet-mapping>
<servlet-name>easyjfservlet-name>
<url-pattern>/ejf/*url-pattern>
servlet-mapping>
<filter>
<filter-name>CharsetFilterfilter-name>
<filter-class>com.easyjf.web.CharsetFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>ignoreparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharsetFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
若web.xml文件配置成功,并把EasyJWeb.jar文件及相关jar文件拷到了web-inf\lib 目录下,启动Web用户服务器。在地址栏中随便输入http://localhost:8080/hello.ejf 类似以.ejf为扩展名的地址。则会得到一个如下的EasyJWeb错误反馈提示信息:
友情提示!
frameworkException: 没有找到处理模板的类:com.easyjweb.action.HelloAction
详细请查询http://www.easyjf.com
如下图所示:

虽然提示框架错误,但既然有了EasyJWeb字眼,这即表示您的EasyJWeb应用程序已经配置成功,下一步就是开始激动人心的EasyJWeb应用开发了。
若没能正确的配置Web.xml,当用户在地址栏中输入EasyJWeb应用程序扩展名的*.ejf时候,将会产生404的错误提示。如下图所示:

四、快速入门与示例
4.1、EasyJWeb版的 Hello World!
下面,我们以一个老掉牙的示例"Hello World!"来开始EasyJWeb的应用程序,我们这里把"Hello World!"改成"喂,您好,EasyJWeb1.0发布了,请支持国产开源项目!",另外还将显示一个系统当前的时间。
第一步,建立java代码程序目录:
在您的源代码目录下,建一个如下的目录com\easyjweb\action。
第二步,写EasyJWeb Action helloAction.java:
在com\easyjweb\action目录下建一个名为helloAction.java的文件。内容如下:
package com.easyjweb.action;
import java.util.Date;
import com.easyjf.web.IWebAction;
import com.easyjf.web.Module;
import com.easyjf.web.Page;
import com.easyjf.web.WebForm;
public class HelloAction implements IWebAction {
public Page execute(WebForm form, Module module) throws Exception {
form.addResult("msg","喂,您好,EasyJWeb1.0发布了,请支持国产开源项目!");//设置VO对象msg的值。
form.addResult("time",new Date());//设置VO对象time的值为当前时间
return new Page("hello", "/hello.html");
}
}
写完后通过使用命令行的javac 或者编译工具编译helloAction.java。若对java的编译不熟悉,请先跳到后面的《EasyJWeb应用开发指南》章有关工具使用的介绍,或者直接执行EasyJWeb的示例程序中的helloAction.class拷到您的web-inf\classes\com\easyjweb\action目录下即可。
第三步,建立EasyJWeb显示页面模板文件
用记事本在/web-inf/easyjweb/建一个名为hello.html的文件,注意保存的时候请选择utf-8编码,helllo.html文件的全部内容如下:
<html>
<head>
<title>我的第一个EasyJWeb程序界面title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
head>
<body>
$!msg<br>
当前时间:$!time
body>
html>
把文件保存为utf-8编码的操作如下所示:

最后一步:启动Tomcat并运行Hello EasyJWeb应用程序
重新启动Tomcat,然后在地址栏中输入http://localhost:8080/hello.ejf 即可看到如下图所示的运行结果:

接下来我们来简单介绍一下这个简单的EasyJWeb应用。在上面这个应用中,我们可以看到,EasyJWeb应用主要包括两个部分:用Java实现的Action以及业务逻辑和一个Html模板(这里并不是使用的单纯的Html,还有Velocity脚本)。这里我们简单介绍一下Action部分,Velocity部分请参考《EasyJWeb-Velocity脚本简明教程》,你可以通过这个地址下载该教程:http://www.easyjf.com/easyjweb/EasyJWeb-Velocity.pdf。
在这个Action实现IWebAction接口,IWebAction接口只有一个方法execute。这个方法有两个参数:WebForm和Module,返回一个Page对象。WebForm负责封装用于用户端显示的数据,程序对WebForm进行处理,并根据Module封装的该模块的配置信息返回一个Page对象(本例中使用的是手动创建一个Page对象,也可以通过使用module.findPage("")方法来获取一个配置好的Page对象),告诉框架返回哪个页面。在程序中用到了WebForm的addResult方法,这个方法主要是用来添加要在客户端显示的数据。以这个程序为例,在执行了form.addResult("time",new Date());这一句之后,就可以在模板中使用$!time来调用这个Date对象。
一个简单的例子,相信大家对EasyJWeb有了大概了解。好的,我们再稍微深入一点,将这个例子改造一下,实现稍微复杂一些的功能。我们在客户端增加一个文本框,输入用户名,提交到后台处理。我们先看看模板页:
<html>
<head>
<title>我的第一个EasyJWeb程序界面title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
head>
<body>
<form action="/hello.ejf">
<input name="userName" type="text" value="$!userName" /><br>
<input type="submit" value="提交" />
form>
$!msg<br>
当前时间:$!time
body>
html>
现在我们看看修改后的Action:
public class HelloAction implements IWebAction {
public Page execute(WebForm form, Module module) throws Exception {
String userName = (String)form.get("userName");
if(userName!=null&&!("".equals(userName))){
form.addResult("msg", ""+userName+",您好,EasyJWeb1.0发布了,请支持国产开源项目!");
}else{
form.addResult("msg", "喂,您好,EasyJWeb1.0发布了,请支持国产开源项目!");
}
form.addResult("time", new Date());
return new Page("hello", "/hello.html");
}
}
这里多了一句“String userName = (String)form.get("userName");”,这里的form.get("userName")用来获取客户端表单域里的userName文本框的值。现在我们来运行一下这个例子。在客户端输入http://localhost:8080/hello.ejf,界面如下:

我们输入“friend”,然后提交,会出现下面的界面:

到这里,相信大家应该了解了在EasyJWeb应用中如何获取客户端数据和向客户端传递数据。接下来我们再深入一点,将这个例子改为用户登录并显示用户的用户名和密码的例子,进一步的了解在EasyJWeb中是如何将对象数据传递给客户端并显示出来的。
在这个例子中我们首先创建一个domain——User.java,代码如下:
public class User {
private String name;
private String password;
public String getName() {
return "tianyi";
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return "123";
}
public void setPassword(String password) {
this.password = password;
}
}
然后我们看看修改后的Action,代码如下:
public class HelloAction implements IWebAction {
public Page execute(WebForm form, Module module) throws Exception {
String userName = (String)form.get("userName");
String password = (String)form.get("passsword");
User user = new User();
if(userName!=null&&!(user.getName().equals(userName))&&password!=null&&user.getPassword().equals(password)){
form.addPo(user);
form.addResult("msg", "登录成功!");
}else{
form.addResult("msg", "登录失败!");
}
form.addResult("time", new Date());
return new Page("hello", "/hello.html");
}
}
这里有一条语句form.addPo(user),这天语句用来将对象user的每一个属性值都传递到客户端。执行了这条语句之后在模板中可以直接使用$!name来显示user的name属性值。这里还可以这样写form.addResult("user", user),然后在模板中这样调用:$!user.name,$!user.password。具体的Velocity用法请参考EasyJF官方网站上的教程。
最后我们修改一下模板文件,代码如下:
<html>
<head>
<title>我的第一个EasyJWeb程序界面title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
head>
<body>
<form action="/hello.ejf">
<input name="userName" type="text" /><br>
<input name="password" type="text" /><br>
<input type="submit" value="提交" />
form>
您的用户名为:$!name<br>
您的密码为 :$!password<br>
$!msg<br>
当前时间:$!time
body>
html>
在浏览器中输入http://localhost:8080/hello.ejf访问,界面如下:

在输入框中分别输入tianyi和123,提交,返回界面如下:

到这里相信大家对EasyJWeb的基本使用方法已经了解了,下面我们将介绍EasyJWeb Tools的用法。
4.2、EasyJWeb Tools
前面我们创建了一个最简单的应用Hello World,现在我们来创建一个完整的EasyJWeb应用。在这里我们将要用到EasyJWeb Tools这个包。EasyJWeb Tools是EasyJWeb的一个重要部分。通常在Action层我们需要对客户端提交的数据做判断,进行流程控制,因此会有大量的if else语句。通过EasyJWeb Tools的业务引擎基本模型,只要编程者按照我们的模型规范进行编程,即可去除烦琐的if else语句。下面我们用一个例子来做简要说明。
AbstractCmdAction、AbstractPageCmdAction、AbstractCrudAction这三个类是EasyJWeb Tools中最重要的三个抽象Action类,这三个类又以AbstractCmdAction为基础类。
AbstractCmdAction类对Action命令进行封装,用于为提供命令式WebAction的写法,用户直接调用,可以减少书写繁锁的if语句。如果不使用AbstractCmdAction,那么就像struts一样每一个操作都要写一个Action,开发效率比较低。如果使用AbstractCmdAction,就可以在一个Action中执行多个操作,只需要在客户端传递一个名为easyJWebCommand的参数即可,AbstractCmdAction会根据这个参数来选择执行哪个方法。假设现在客户端传递来的参数值是save,那么将会执行doSave()方法,其它的以此类推。
AbstractPageCmdAction类继承自AbstractCmdAction类。AbstractPageCmdAction对返回的Page对象进行了处理,用来支持更加灵活的参数调用。使用这个类可以实现零配置,并且不用显示的返回Page对象,它会根据类名、命令参数值和配置好的view路径来自动创建一个Page对象。
AbstractCrudAction继承自AbstractPageCmdAction类,AbstractCrudAction类中对一些常用的、通用性比较强的业务逻辑方法如简单的添删改查操作进行了封装。如doEdit()方法,这个方法通常用来指向一个编辑页面,通用性很高,因此做了封装,类似的还有很多,这里不一一列举。下面我们将分别举例对这三个类的用法进行简要讲解。
这是一个数据字典的添删改查应用,这里我们对只对字典目录进行添删改查操作。我们将分别使用这三个类来实现这个应用,比较这几个类的不同作用。现在我们来看看使用AbstractCmdAction类时的Action,以下是Action的全部代码:
public class SystemDictionaryAction extends AbstractCmdAction {
private ISystemDictionaryService service;
public void setService(ISystemDictionaryService service) {
this.service = service;
}
public Page doList(WebForm form, Module module){
QueryObject query = new QueryObject();
form.toPo(query);
IPageList pageList = service.getSystemDictionaryBy(query);
CommUtil.saveIPageList2WebForm(pageList, form);
return new Page("list", "/news/dictionaryList.html");
}
public Page doAdd(WebForm form, Module module){
return new Page("edit", "/news/dictionaryEdit.html");
}
public Page doEdit(WebForm form, Module module){
Long id = Long.parseLong(CommUtil.null2String(form.get("id")));
form.addPo(this.service.getSystemDictionary(id));
return new Page("edit", "/news/dictionaryEdit.html");
}
public Page doSave(WebForm form, Module module){
SystemDictionary dic = form.toPo(SystemDictionary.class);
this.service.addSystemDictionary(dic);
return this.doList(form, module);
}
public Page doDel(WebForm form, Module module){
Long id = Long.parseLong(CommUtil.null2String(form.get("id")));
this.service.delSystemDictionary(id);
return this.doList(form, module);
}
public Page doUpdate(WebForm form, Module module){
Long id = Long.parseLong(CommUtil.null2String(form.get("id")));
SystemDictionary dic = this.service.getSystemDictionary(id);
form.toPo(dic);
this.service.updateSystemDictionary(id, dic);
return this.doList(form, module);
}
}
AbstractCmdAction实现了IWebAction,在它的execute方法里边根据easyJWebCommand的值来判断应该执行哪个方法。因此继承自这个类的Action就不需要再实现execute方法,只需要编写与操作相对应的doXxxx()方法。以doSave()方法为例,当easyJWebCommand参数值为save时就会执行doSave()方法。在这个Action中,代码已经比较简洁了,也没有了if else语句,并且在一个Action里实现了关于SystemDictionary 这个对象的添删改查操作。但是我们的目标是要越来越简单,越来越快捷,我们并不满足于此,接下来我们看看AbstractPageCmdAction类的使用。
下面同样是数据字典的添删改查操作,不同的是这个Action继承自AbstractPageCmdAction类,我们先看看这个Action的代码:
public class SystemDictionaryAction extends AbstractPageCmdAction {
private ISystemDictionaryService service;
public void setService(ISystemDictionaryService service) {
this.service = service;
}
public void doList(WebForm form, Module module){
QueryObject query = new QueryObject();
form.toPo(query);
IPageList pageList = service.getSystemDictionaryBy(query);
CommUtil.saveIPageList2WebForm(pageList, form);
}
public void doEdit(WebForm form, Module module){
Long id = Long.parseLong(CommUtil.null2String(form.get("id")));
form.addPo(this.service.getSystemDictionary(id));
}
public void doSave(WebForm form, Module module){
SystemDictionary dic = form.toPo(SystemDictionary.class);
this.service.addSystemDictionary(dic);
}
public void doDel(WebForm form, Module module){
Long id = Long.parseLong(CommUtil.null2String(form.get("id")));
this.service.delSystemDictionary(id);
}
public void doUpdate(WebForm form, Module module){
Long id = Long.parseLong(CommUtil.null2String(form.get("id")));
SystemDictionary dic = this.service.getSystemDictionary(id);
form.toPo(dic);
this.service.updateSystemDictionary(id, dic);
}
}
AbstractPageCmdAction 类继承自AbstractCmdAction,这个类里边重写了execute方法,它先调用父类的execute方法,然后对父类的execute方法返回的值进行判断,如果为空并且easyJWebCommand参数值不为空,就会根据调用的Action类的名字和easyJWebCommand参数的值来创建一个Page对象返回给框架。
这个Action与前一个Action相比,可以看出来,所有的doXxxx()方法都是void的了,不再return一个Page对象了。这就是AbstractPageCmdAction的作用,它会根据你调用的Action类的名字和客户端提交进来的命令参数的值自动创建一个Page对象,从而实现零配置,开发人员可以专注于业务逻辑,不用再关心流程控制。这样一来开发是不是又变得简单了一些、快捷了一些?但是我们的目标是不断前进的,我们不会满足现状,我们要越来越快。
好的,激动人心的时刻到来了,前面我们讲解的两个类虽然使开发已经很方便了,但是我们可以看到,Action中还充斥着大量的form.get()、form.addResult()等方法,并且很多方法中都有着这样一些相同的代码,那么我们是否能够再封装一下呢?现在我们来看看AbstractCrudAction 类的使用。还是同样的应用:
public class SystemDictionaryAction extends AbstractCrudAction {
private ISystemDictionaryService service;
public void setService(ISystemDictionaryService service) {
this.service = service;
}
@SuppressWarnings("unchecked")
protected Class entityClass() {
return SystemDictionary.class;
}
protected Object findEntityObject(Serializable id) {
return service.getSystemDictionary((Long) id);
}
protected IPageList queryEntity(IQueryObject queryObject) {
return service.getSystemDictionaryBy(queryObject);
}
protected void removeEntity(Serializable id) {
service.delSystemDictionary((Long) id);
}
protected void saveEntity(Object object) {
service.addSystemDictionary((SystemDictionary) object);
}
protected void updateEntity(Object object) {
service.updateSystemDictionary(((SystemDictionary) object).getId(),(SystemDictionary) object);
}
}
我们可以看到Action的代码少了很多,业务逻辑的处理完全封装了,每个操作都只需要一条调用语句即可。现在我们来分析一下代码。首先,Action里多出了一个方法entityClass(),这个方法是AbstractCrudAction 里申明的一个抽象方法,它在没个Action里边的实现都不一样,分别返回各自要操作的类,提供给AbstractCrudAction 里实现的一些业务逻辑方法使用。
接下来,我们会发现方法的命名也和以前不一样了,这里没有了doXxxx()方法,取而代之的是xxxxEntity()方法。这些方法都是在父类AbstractCrudAction 里申明的抽象方法,在每个不同的Action里实现,AbstractCrudAction 里的业务逻辑方法调用这些不同的实现分别执行不同的操作,巧妙的对通用性比较强的代码进行了封装,使开发变得更加简单快捷。假设命令参数值为save,在这里将不再执行doSave()方法,而是执行saveEntity()方法。这里并不是去掉了doXxxx()方法,而是将这些方法放到了AbstractCrudAction类里边,这里的saveEntyti()方法会被父类的doSave()方法调用,因此实际上还是调用了doSave()方法,只不过这个方法不需要开发人员自己来申明和实现了。