电竞比分网-中国电竞赛事及体育赛事平台

分享

超輕量級(jí)MVC框架的設(shè)計(jì)和實(shí)現(xiàn) (1)

 CevenCheng 2011-07-29

前段時(shí)間準(zhǔn)備做一個(gè)小網(wǎng)站,但是又不想用Spring/Struts/WebWork這樣的大塊頭,因此決定自己寫(xiě)一個(gè)MVC框架?;?天左右時(shí)間完成,目前運(yùn)行良好,整個(gè)MVC框架僅21KB,感興趣的朋友可以從http://code.google.com/p/lightweight-mvc/downloads/list下載完整的源代碼和jar包。

設(shè)計(jì)目標(biāo):

一個(gè)最簡(jiǎn)單最小巧的MVC框架,花哨的功能一個(gè)不要,越簡(jiǎn)潔越好,并且不使用XML配置文件,而是完全用Java 5注解配置。

功能列表:

組件必須用IoC配置;

處理HTTP請(qǐng)求的Action,類(lèi)似WebWork每個(gè)請(qǐng)求都生成一個(gè)新實(shí)例,并自動(dòng)填充屬性;

類(lèi)似Filter的Interceptor機(jī)制,但是在IoC容器中配置;

統(tǒng)一的異常處理;

多視圖支持。

由于組件需要用IoC容器配置,因此,第一步就是尋找小巧的IoC容器,Google Guice是一個(gè)很不錯(cuò)的選擇,并且完全用Java 5注解配置組件。這個(gè)MVC框架唯一依賴(lài)的也就是Guice和Commons Logging兩個(gè)jar包,如果使用Velocity作為視圖則還需要Velocity的jar包。

下一步,開(kāi)始設(shè)計(jì)各主要功能類(lèi):

負(fù)責(zé)處理Http請(qǐng)求的Action類(lèi)必須實(shí)現(xiàn)的Action接口:

package com.javaeedev.lightweight.mvc;

public interface Action {

ModelAndView execute() throws Exception;

}

從WebWork抄過(guò)來(lái),不過(guò)返回值由String改成了ModelAndView(從Spring抄過(guò)來(lái)的),好處是不必再次根據(jù)String查找視圖的絕對(duì)路徑,直接在ModelAndView中包含了。用Spring的MVC其實(shí)可以發(fā)現(xiàn),ModelAndView同時(shí)包含一個(gè)Model(本質(zhì)是一個(gè)Map)和View的路徑,減少了Struts和WebWork需要的一個(gè)XML映射文件,而維護(hù)XML配置文件是一件相當(dāng)令人頭疼的問(wèn)題,往往改了代碼還要改配置,索性寫(xiě)死在代碼中得了,視圖路徑又不會(huì)經(jīng)常改變,沒(méi)必要為了額外的靈活性給自己搞一堆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>

}

這個(gè)完全是從Spring MVC抄過(guò)來(lái)的,Map改成了泛型,View路徑可以以"redirect:"開(kāi)頭表示重定向,這個(gè)和Spring MVC一致。雖然直接調(diào)用HttpServletResponse也可以重定向,但是遇到事務(wù)處理起來(lái)會(huì)很麻煩,還是讓MVC框架自己來(lái)處理會(huì)好一些。

WebWork的Action設(shè)計(jì)的好處是大大簡(jiǎn)化了參數(shù)的綁定,不過(guò)很多時(shí)候也需要在Action中訪問(wèn)HttpSession等對(duì)象,因此還需要設(shè)計(jì)一個(gè)ActionContext類(lèi),通過(guò)ThreadLocal讓Action對(duì)象能輕易地訪問(wèn)到這些對(duì)象:

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;
}

}

接下來(lái)是定義類(lèi)似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對(duì)象和FilterChain是一樣的,它允許一個(gè)攔截器是否將請(qǐng)求繼續(xù)交給下一攔截器處理,還是中斷當(dāng)前請(qǐng)求的處理:

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,這個(gè)也抄自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>

}

第一個(gè)版本支持JSP和Velocity兩種View,其實(shí)支持其他的View完全是可擴(kuò)展的,只需要參考現(xiàn)有的兩種ViewResolver的實(shí)現(xiàn)再寫(xiě)一個(gè)實(shí)現(xiàn)即可,例如支持FreeMarker的ViewResolver。

到此為止,提供給客戶(hù)端的API準(zhǔn)備完畢。下一步是如何實(shí)現(xiàn)這些API。雖然概念和結(jié)構(gòu)都來(lái)自WebWork和Spring,但是其具體實(shí)現(xiàn)卻沒(méi)有參考他們的源代碼,因?yàn)樽x大塊頭的源碼本身就是一件非常費(fèi)力的事情,還不如自己身體力行,寫(xiě)代碼往往比讀懂代碼更快。

后面我們會(huì)講述如何實(shí)現(xiàn)該MVC框架。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多