前言
做过不少的J2EE项目,由于个人的习惯问题,很希望能保持表现层代码的高度简洁,也希望页面的HTML能尽可能地保持原貌(不知是否可称之为洁癖^_^)。从以前就一直使用的Struts到后来使用过一段时间的JSF,都为自己的开发工作带来了非常大的便利和喜悦,但由于前面所说的个人习惯的原因,对于这两个非常优秀的框架还是感到稍嫌繁琐,特别是那些几乎必须使用的侵入性极高的页面标签,如果不使用的话就几乎发挥不出框架的优势。大多数时候,个人只是需要项目的总体结构能实现MVC而已,至于页面上的数据显示、合法值验证等,则交由标准的JSTL标签、模版组件及JS验证框架来做,当然,要求安全性高的合法值验证,还需要框架在后台验证多一次。于是前段时间便萌发了开发一个简单的MVC框架来自用的想法,毕竟它的原理并不复杂,而且自己也从来都不让正在使用的框架包揽太多的东西,所以只需要去实现必需的功能,运行效率必然可以提高,在以后的开发上也能让自己省多点事。既然要求简单(主要是使用上很简单),所以就起了个贴切的名字——SimpleFace。
事实上,在开发的过程中,有些原本不打算要的功能还是觉得不可或缺的,否则就丝毫没有应用的价值了。当然,这些功能是应用上的必备功能,在使用上的简单化和非侵入性上与我的原则始终保持一致。最终,SimpleFace实现了请求路径与处理方法及处理结果转发的可配置化,同时支持Freemarker模版的使用;支持国际化资源文件的配置使用;支持极其方便的文件上传;支持类似Struts的通配符配置以简化配置工作。
由于开发SimpleFace的动机只是为了自用,而且个人的水平的确有限,难免会有很多不足,相比那些标杆框架更是显得幼稚,所以不敢奢望让很多人去使用它,发布出来完全是为了分享与交流。当然,如果您觉得可以在您的项目中使用它,我会感到很高兴,如果您能不啬给出改进的建议,我会非常感谢您。也许有人会觉得做这个东西纯粹是无聊,我也明白造轮子的说法,我只是想把自己所学的那丁点知识转化为一点成果,对日后的开发工作中起到一点帮助。目前我还不能保证这个框架的稳定性,因为才做出来不久,我还没有应用到我的项目上,但有经过几轮的各项测试,暂时没有发现什么问题。因此还是随着时间的推移,来不断发现问题并予以改善吧。
附件:
1、SimpleFace-1.0_bin.rar(运行所需JAR包、使用例子工程[Eclipse]、API文档)
2、SimpleFace-1.0_src.rar(SimpleFace源码)
一、SimpleFace工作原理示意图
由于手头上的画图工具无法完全表达出我想要的效果,所以将就着画出了下面这张图。
不难看出,SimpleFace的结构与Struts的结构有点类似,这种经典合理的结构非我辈可以突破。
二、快速上手
以附件例子中的用户登录演示部份内容来讲解。
(1)配置
1、将核心的几个jar(SimpleFace-1.0.jar、依赖dom4j.jar、commons-logging.jar、jaxen.jar)放置到WEB-INF\lib目录下;
2、web.xml配置
<servlet>
<servlet-name>simpleface</servlet-name>
<servlet-class>com.funper.simpleface.action.DispatcherServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/simple-face.xml</param-value>
</init-param>
<!--在这里还可以加上其他三个可选参数:采用何种编码、允许上传文件最大字节数、允许上传文件类型。附件中的例子有提供-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>simpleface</servlet-name>
<url-pattern>*.sf</url-pattern>
</servlet-mapping>
这样就使得所有后缀为.sf的请求URI都由SimpleFace来处理。
3、SimpleFace的核心配置文件simple-face.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE simple-face PUBLIC "-//SimpleFace//DTD 1.0//EN"
"http://www.funper.com/dtds/simple-face1.0.dtd">
<!--已由上面的DTD文档定义配置文档的规范-->
<simple-face>
<global>
<!--全局转发配置(如果在Action中找不到对应的result,则在这里找)[可选]-->
<results>
<result name="success">/common/success.jsp</result>
<result name="failure">/common/failure.jsp</result>
</results>
<!--Freemarker模版文件目录配置(如果配置此项,则必须将freemarker.jar引入工程)[可选]-->
<!--freemarker directory="/WEB-INF/templates"/-->
<!--国际化资源文件配置[可选]-->
<!--resources baseName="net.sf.demo.Messages"/-->
</global>
<!--action配置-->
<actions>
<!--action的class别名配置(如果设置了别名,则在action的class属性中只需填写别名而无需填写类全名)[可选]-->
<classAlias name="user" class="net.sf.demo.UserAction"/>
<!--用户登录演示, 支持通配符配置, 在附件中的例子有演示-->
<action path="/login" method="login" class="user">
<!--result的type属性可选(freemarker|redirect|forward,不选默认为forward)-->
<result name="success" type="redirect">/index.jsp</result>
</action>
</actions>
</simple-face>
有Struts使用经验的朋友一定能一眼就看得明白simple-face.xml的内容,加上还算详细的注释,不需要多说些什么了。在配置文件里有些标签的名称我沿用了Struts一惯使用的名称,我可不想搞出一些词不达意的新的名称出来以示自己有什么独特的创举,毕竟如果我没有使用Struts和JSF的经验的话,我是不可能开发得出来这个所谓的框架的。
(2)功能实现
在配置文件中可以看到,用户登录的Action配置,请求path为“/login”,处理请求的类的别名为”user“,对应的类为“net.sf.demo.UserAction”,处理请求的类方法名为“login”。
1、登录页面login.jsp的表单内容
<form name="form1" method="post" action="login.sf">
<table width="300" border="0">
<tr align="center">
<td height="26" colspan="2" align="center">::用户登录::</td>
</tr>
<tr>
<td width="84" height="26" align="right">登录帐号:</td>
<td width="205"><input type="text" name="user.userName"></td>
</tr>
<tr>
<td width="84" height="26" align="right">登录密码:</td>
<td bgcolor="#FFFFFF"><input type="password" name="user.userPwd"></td>
</tr>
<tr align="center">
<td height="30" colspan="2" bgcolor="#FFFFFF">
<input type="submit" name="submitButton" value="登 录">
</td>
</tr>
</table>
</form>
2、用户数据模型User.java
public class User {
private String userName;//用户名
private String userPwd; //用户密码
public void setUserName(String name) {
this.userName = name;
}
public void setUserPwd(String pwd) {
this.userPwd = pwd;
}
public String getUserName() {
return userName;
}
public String getUserPwd() {
return userPwd;
}
}
3、UserAction.java
import com.funper.simpleface.DataModel;
import com.funper.simpleface.action.FaceContext;
public class UserAction {
private static final String SUCCESS="success";
private static final String FAILURE="failure";
@DataModel
private User user; //用户数据模型——对于数据模型来讲,要与提交的参数值绑定就必须使用@DataModel注解,否则不能绑定。如果直接把用户名和用户密码定义在这里,则不需要使用注解)
public void setUser(User user)
{
this.user=user;
}
/**
* 登录方法
* @param context 方法上下文参数
* @return String
*/
public String login(FaceContext context)
{
//关于上下文参数FaceContext,可以从中获取到HttpServletRequest、HttpServletResponse、ServletContext、国际化资源ResourceMessages、文件上传监控器UploadWatcher等对象,在附件中的例子均有体现。
if("".equals(user.getUserName())||"".equals(user.getUserPwd()))
{
//登录失败
return FAILURE;
}
else
{
//登录成功
}
return SUCCESS;
}
}
到此,已经展示了SimpleFace的基本使用方法。可以看到, 较Struts1.x而言,所需代码量减少了不是一般的多,配置文件内容也很少,而且还同样支持使用通配符来配置。较Struts2.0而言,所需代码量差别不大。Struts2.0功能非常强大,所耗资源也不小,如果项目不大的话,又想使用MVC框架,SimpleFace的性能应该是非常高的,因为它相对Struts来说,实现上非常简单,执行步骤与所需要加载的类也非常少。我已经使用JMeter和JProfilter来做过几轮测试,验证了这一点。
三、文件上传
1、上传页面upload.jsp的表单内容
<form name="form1" method="POST" enctype="multipart/form-data" action="/Upload/uploadBySign.sf">
title:<input name="title" type="text" size="40"><br>
File:<input type="file" name="myFile"><br>
<input type="submit" name="submitButton" value="提 交">
</form>
2、配置
<action path="/Upload/uploadBySign" method="uploadBySign" class="net.sf.demo.UploadAction"/>
3、UploadAction.java
import java.io.File;
import java.io.IOException;
import com.funper.simpleface.upload.UploadFile;
import com.funper.simpleface.action.FaceContext;
public class UploadAction {
private String title; //对应<input type="text" name="title">
private UploadFile myFile; //对应<input type="file" name="myFile">
public void setTitle(String title)
{
this.title=title;
}
public void setMyFile(UploadFile myFile)
{
this.myFile=myFile;
}
//-----------------------------------------------------
/**
* 上传文件
* @param context
* @return String
*/
public String uploadBySign(FaceContext context)
{
//输出提交的title参数值
System.out.println("title="+title);
/**
* 如果在web.xml中配置SimpleFace的DispatcherServlet时,
* 有配置允许上传文件的最大字节数或允许上传文件的类型,
* 则可以利用文件上传监控器来获取上传文件属性是否已经超出了限定范围, 并作出相应处理。
**/
/*if(context.getUploadWatcher().isExtensionExceeded) //上传文件类型超出了限定范围
{
//处理
}
else if(context.getUploadWatcher().isMaxlengthExceeded) //上传文件尺寸超出了限定范围
{
//处理
}*/
if(myFile!=null)
{
//定义文件保存目录
File saveDir=new File(context.getServletContext().getRealPath("/")+"Upload/upload_files");
try {
myFile.save(saveDir); //保存上传文件
System.out.println("上传文件保存在==>"+saveDir.getAbsolutePath()+"\\"+myFile.getFileName());
} catch (IOException e) {
e.printStackTrace();
return "failure";
}
}
else
{
return "failure";
}
return "success";
}
}
文件上传这一块是不是很简单?多文件上传可以把接收对象UploadFile定义为数组,在附件中的例子有演示。
四、国际化
1、资源文件(以中、英文为例),假设放在net.sf.demo包下面
(1).Messages_zh_CN.properties
login.success={0} 登录成功!
login.failure=登录失败!
login.signout=登录已注销!
(2).Messages_en.properties
login.success={0} Login is successed!
login.failure=Login is failured!
login.signout=Login is canceled!
2、配置
<global>
<resources baseName="net.sf.demo.Messages"/>
</global>
3、方法
public String test(FaceContext context)
{
//中文客户端下输出“登录失败!”
System.out.println(context.getResourceMessages().getValue("login.failure"));
//中文客户端下输出“张三 登录成功!”
System.out.println(
context.getResourceMessages().getValue(
"login.success",
new Object[]{"张三"})
);
return null;
}
五、使用Freemarker
1、模版文件目录配置
<global>
<freemarker directory="/WEB-INF/templates"/>
</global>
2、result配置
<result name="view2" type="freemarker">/viewNews.ftl</result>
3、方法
public String freemarkerViewNews(FaceContext context)
{
News news=new News();
Map<String, Object> dataMap=new HashMap<String, Object>();
dataMap.put("news", news);
//设置Freemarker模版数据
context.setFreemarkerDataMap(dataMap);
return "view2";
}
六、结束
SimpleFace能做的基本上就这么多了。在这里要提一下合法值验证的问题,目前我还没有去实现这部份的完整功能,而是仅仅验证提交上来的值是否为后台定义的类型,如果不符合定义则不会继续执行类方法。我通常是使用前台的JS验证框架来做验证的,有使用自己实现的,也有使用第三方的如jsvalidate。如果像Struts那样使用自定义标签或者再加上一个写验证规则的配置文件,这个功能倒是不难实现,但我因为极力避免对页面的侵入和增加使用者的学习成本,所以在想到更好的办法之前,我不想去实现。欢迎各位大佬拍砖,没有批评指正,则不会有进步!