超轻量级MVC框架的设计和实现 (1)

<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog336280.html" frameborder="0" width="336" scrolling="no" height="280"></iframe>

前段时间准备做一个小网站,但是又不想用Spring/Struts/WebWork这样的大块头,因此决定自己写一个MVC框架。花了3天左右时间完成,目前运行良好,整个MVC框架仅21KB,感兴趣的朋友可以从http://code.google.com/p/lightweight-mvc/downloads/list下载完整的源代码和jar包。

设计目标:

一个最简单最小巧的MVC框架,花哨的功能一个不要,越简洁越好,并且不使用XML配置文件,而是完全用Java 5注解配置。

功能列表:

组件必须用IoC配置;

处理HTTP请求的Action,类似WebWork每个请求都生成一个新实例,并自动填充属性;

类似Filter的Interceptor机制,但是在IoC容器中配置;

统一的异常处理;

多视图支持。

由于组件需要用IoC容器配置,因此,第一步就是寻找小巧的IoC容器,Google Guice是一个很不错的选择,并且完全用Java 5注解配置组件。这个MVC框架唯一依赖的也就是Guice和Commons Logging两个jar包,如果使用Velocity作为视图则还需要Velocity的jar包。

下一步,开始设计各主要功能类:

负责处理Http请求的Action类必须实现的Action接口:

package com.javaeedev.lightweight.mvc;

public interface Action {

ModelAndView execute() throws Exception;

}

从WebWork抄过来,不过返回值由String改成了ModelAndView(从Spring抄过来的),好处是不必再次根据String查找视图的绝对路径,直接在ModelAndView中包含了。用Spring的MVC其实可以发现,ModelAndView同时包含一个Model(本质是一个Map)和View的路径,减少了Struts和WebWork需要的一个XML映射文件,而维护XML配置文件是一件相当令人头疼的问题,往往改了代码还要改配置,索性写死在代码中得了,视图路径又不会经常改变,没必要为了额外的灵活性给自己搞一堆XML配置文件。

Action返回的ModelAndView:

package com.javaeedev.lightweight.mvc;

public final class ModelAndView {

private String view;
private Map<string object> model;</string>

/**
* Construct a View with empty model.
*
* @param view View's logic name.
*/
public ModelAndView(String view) {
this.view = view;
this.model = Collections.emptyMap();
}

/**
* Construct a View with model.
*
* @param view View's logic name.
* @param model Model as a Map.
*/
public ModelAndView(String view, Map<string object> model) {<br> this.view = view;<br> this.model = model;<br> }</string>

/**
* Return View.
*
* @return View's logic name.
*/
public String getView() {
return view;
}

/**
* Return model.
*
* @return Model as a Map.
*/
public Map<string object> getModel() {<br> return model;<br> }</string>

}

这个完全是从Spring MVC抄过来的,Map改成了泛型,View路径可以以"redirect:"开头表示重定向,这个和Spring MVC一致。虽然直接调用HttpServletResponse也可以重定向,但是遇到事务处理起来会很麻烦,还是让MVC框架自己来处理会好一些。

WebWork的Action设计的好处是大大简化了参数的绑定,不过很多时候也需要在Action中访问HttpSession等对象,因此还需要设计一个ActionContext类,通过ThreadLocal让Action对象能轻易地访问到这些对象:

package com.javaeedev.lightweight.mvc;

public final class ActionContext {

private static ThreadLocal<actioncontext> contextThreadLocal = new ThreadLocal<actioncontext>();</actioncontext></actioncontext>

/**
* Get current ActionContext.
*
* @return ActionContext.
*/
public static ActionContext getActionContext() {
return contextThreadLocal.get();
}

private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private ServletContext context;

/**
* Initiate all servlet objects as thread local.
*
* @param request HttpServletRequest object.
* @param response HttpServletResponse object.
* @param session HttpSession object.
* @param context ServletContext object.
*/
static void setActionContext(HttpServletRequest request, HttpServletResponse response, HttpSession session, ServletContext context) {
ActionContext actionContext = new ActionContext();
actionContext.setRequest(request);
actionContext.setResponse(response);
actionContext.setSession(session);
actionContext.setServletContext(context);
contextThreadLocal.set(actionContext);
}

/**
* Remove all servlet objects from thread local.
*/
static void remove() {
contextThreadLocal.remove();
}

/**
* Get HttpServletRequest object.
*
* @return HttpServletRequest object.
*/
public HttpServletRequest getRequest() {
return request;
}

/**
* Set HttpServletRequest object.
*
* @param request HttpServletRequest object.
*/
void setRequest(HttpServletRequest request) {
this.request = request;
}

/**
* Get HttpServletResponse object.
*
* @return HttpServletResponse object.
*/
public HttpServletResponse getResponse() {
return response;
}

/**
* Set HttpServletResponse object.
*
* @param response HttpServletResponse object.
*/
void setResponse(HttpServletResponse response) {
this.response = response;
}

/**
* Get HttpSession object.
*
* @return HttpSession object.
*/
public HttpSession getSession() {
return session;
}

/**
* Set HttpSession object.
*
* @param session HttpSession object.
*/
void setSession(HttpSession session) {
this.session = session;
}

/**
* Get ServletContext object.
*
* @return ServletContext object.
*/
public ServletContext getServletContext() {
return context;
}

/**
* Set ServletContext object.
*
* @param context ServletContext object.
*/
void setServletContext(ServletContext context) {
this.context = context;
}

}

接下来是定义类似Filter功能的Interceptor接口:

package com.javaeedev.lightweight.mvc;

/**
* Intercept action's execution like servlet Filter, but interceptors are
* configured and managed by IoC container. Another difference from Filter
* is that Interceptor is executed around Action's execution, but before
* rendering view.
*
* @author Xuefeng
*/
public interface Interceptor {

/**
* Do intercept and invoke chain.doInterceptor() to process next interceptor.
* NOTE that process will not continue if chain.doInterceptor() method is not
* invoked.
*
* @param action Action instance to handle http request.
* @param chain Interceptor chain.
* @throws Exception If any exception is thrown, process will not continued.
*/
void intercept(Action action, InterceptorChain chain) throws Exception;

}

InterceptorChain对象和FilterChain是一样的,它允许一个拦截器是否将请求继续交给下一拦截器处理,还是中断当前请求的处理:

package com.javaeedev.lightweight.mvc;

/**
* Holds all interceptors as a chain.
*
* @author Xuefeng
*/
public interface InterceptorChain {

/**
* Apply next interceptor around the execution of Action.
*
* @param action Target Action to execute.
* @throws Exception Any exception if error occured.
*/
void doInterceptor(Action action) throws Exception;

}

最后是支持多种View的ViewResolver,这个也抄自Spring MVC:

package com.javaeedev.lightweight.mvc;

import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* To resolve and render a view.
*
* @author Xuefeng
*/
public interface ViewResolver {

/**
* Init this ViewResolver.
*
* @param context ServletContext object that holds information of current
* web application.
* @throws ServletException If init failed.
*/
void init(ServletContext context) throws ServletException;

/**
* To resolve view's name and render view if necessary.
*
* @param view View's logic name.
* @param model Model represent as a generic Map.
* @param request HttpServletRequest object.
* @param response HttpServletResponse object.
* @throws ServletException If any ServletException occur.
* @throws IOException If any IOException occur.
*/
void resolveView(String view, Map<string object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;</string>

}

第一个版本支持JSP和Velocity两种View,其实支持其他的View完全是可扩展的,只需要参考现有的两种ViewResolver的实现再写一个实现即可,例如支持FreeMarker的ViewResolver。

到此为止,提供给客户端的API准备完毕。下一步是如何实现这些API。虽然概念和结构都来自WebWork和Spring,但是其具体实现却没有参考他们的源代码,因为读大块头的源码本身就是一件非常费力的事情,还不如自己身体力行,写代码往往比读懂代码更快。

后面我们会讲述如何实现该MVC框架。




一个非常简单的MVC框架实现了类似Spring MVC的基本功能。 1、包括自动扫描绑定映射路径,只要在web.xml中指定扫描包,系统启动后会将请求url绑定到指定的处理方法上。如: 在web.xml中定义如下: <context-param> <param-name>ScanPackage</param-name> <param-value>com.mvc.controller</param-value> </context-param> 容器在启动时候,会将com.mvc.controller下所有映射路径绑定处理方法上,假如在扫描包中定义下列类: import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.hxw.simple.light.mvc.annotation.MappingMethodAnnotation; import com.hxw.simple.light.mvc.view.SimpleModelView; import com.mvc.verification.VerificationCode; public class LoginController { @MappingMethodAnnotation(mappingMethod = "/login.do") public String login(HttpServletRequest request, HttpServletResponse response) { return "login/main"; } @MappingMethodAnnotation(mappingMethod = "/loginvm.do") public SimpleModelView loginView(HttpServletRequest request, HttpServletResponse response) { SimpleModelView mv = new SimpleModelView("login/mainmv"); Map<String, String> m = new HashMap<String, String>(); m.put("beij", "北京"); m.put("sha", "上海"); m.put("nanj", "南京"); mv.setAttribute("city", m); return mv; } @MappingMethodAnnotation(mappingMethod = "/Verify.do") public void service(HttpServletRequest arg0, HttpServletResponse arg1) throws IOException { HttpServletRequest request = (HttpServletRequest) arg0; HttpServletResponse response = (HttpServletResponse) arg1; response.setContentType("image/jpeg"); response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); VerificationCode vCode = new VerificationCode(); BufferedImage bufferImage = vCode.getImageData(); HttpSession session = request.getSession(); response.addCookie(new Cookie("JSSESIONID", session.getId())); ServletOutputStream responseOutputStream = response.getOutputStream(); ImageIO.write(bufferImage, "JPEG", responseOutputStream); responseOutputStream.flush(); responseOutputStream.close(); } } 那么在接到url请求如:http://localhost:8080/TestSimpleMVC/loginvm.do 会调用指定的方法处理。 2、支持视图定义,在web.xml定义了视图路径后: <servlet> <servlet-name>DelegateForwardServlet</servlet-name> <servlet-class>com.hxw.simple.light.mvc.servlet.DelegateForwardServlet</servlet-class> <init-param> <param-name>prefix</param-name> <param-value>/WEB-INF/view/</param-value> </init-param> <init-param> <param-name>suffix</param-name> <param-value>.jsp</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>DelegateForwardServlet</servlet-name> <url-pattern>/SYS_FORWARD_URL.fo</url-pattern> </servlet-mapping> 只需在执行完方法后,返回字符串:如return "login/main"就会跳转到指定视图,还可以在视图中用EL表单式访问modelview数据,例子如: SimpleModelView mv = new SimpleModelView("login/mainmv"); Map<String, String> m = new HashMap<String, String>(); m.put("beij", "北京"); m.put("sha", "上海"); m.put("nanj", "南京"); mv.setAttribute("city", m); return mv; 3、支持数据参数自动绑定如: @ParamAttribute(type = ParamaAttributeType.REQUEST, key = "userPassword") String s, @ParamAttribute(type = ParamaAttributeType.SESSION, key = "11212") String s1, @ParamAttribute(type = ParamaAttributeType.SERVLETCONTEXT, key = "32312") String s2, @ParamAttribute(type = ParamaAttributeType.REQUEST, key = "userNames") String[] s3 系统根据参数指定的范围,这指定范围内赋值到参数上,你还可以直接使用javabean做参数绑定,如: public class User extends PaginatedHelper { private static final long serialVersionUID = -8225389551152428829L; private String userName; private String userPassword; public User() { super(); } public User(String userName, String userPassword) { super(); this.userName = userName; this.userPassword = userPassword; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } 在方法上带上 Uer user后,属性名称相同的数据就会赋值到javabean上。不必再使用繁琐的user.setUserName(request.getParameter("userName")); 3、简便的jdbc操作 有查询模板QueryTemplate,命名查询NamedQueryTemplate及bean作为参数的BeanQueryTemplate等。支持返回javabean类型,javabean列表, MAP类型,map列表类型,统计结果queryForInt等。
天梯(tianti) Java 轻量级的 CMS 解决方案-天梯。天梯是一个用 Java 相关技术搭建的后台 CMS 解决方案,用户可以结合自身业务进行相应扩展,同时提供了针对 dao、service 等的代码生成工具。技术选型:Spring Data JPA、Hibernate、Shiro、 Spring MVC、Layer、MySQL 等。 简介: 1、天梯是一款使用Java编写的免费的轻量级CMS系统,目前提供了从后台管理到前端展现的整体解决方案。 2、用户可以不编写一句代码,就制作出一个默认风格的CMS站点。 3、前端页面自适应,支持PC和H5端,采用前后端分离的机制实现。后端支持天梯蓝和天梯红换肤功能。 4、项目技术分层明显,用户可以根据自己的业务模块进行相应地扩展,很方便二次开发。 核心框架Spring Framework 4.2.5.RELEASE 安全框架:Apache Shiro 1.3.2 视图框架Spring MVC 4.2.5.RELEASE 数据库连接池:Tomcat JDBC 缓存框架:Ehcache ORM框架Spring Data JPA、hibernate 4.3.5.Final 日志管理:SLF4J 1.7.21、Log4j 编辑器:ueditor 工具类:Apache Commons、Jackson 2.8.5、POI 3.15 view层:JSP 数据库:mysql、oracle等关系型数据库 前端 dom : Jquery 分页 : jquery.pagination UI管理 : common UI集成 : uiExtend 滚动条 : jquery.nicescroll.min.js 图表 : highcharts 3D图表 :highcharts-more 轮播图 : jquery-swipe 表单提交 :jquery.form 文件上传 :jquery.uploadify 表单验证 :jquery.validator 展现树 :jquery.ztree html模版引擎 :template
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值