久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

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

 wuxyu 2008-07-07

在設(shè)計(jì)完API后,,我們就需要實(shí)現(xiàn)這個(gè)MVC框架。MVC框架的核心是一個(gè)DispatcherServlet,用于接收所有的HTTP請(qǐng)求,并根據(jù)URL選擇合適的Action對(duì)其進(jìn)行處理。在這里,和Struts不同的是,所有的組件均被IoC容器管理,,因此,DispatcherServlet需要實(shí)例化并持有Guice IoC容器,,此外,,DispatcherServlet還需要保存URL映射和Action的對(duì)應(yīng)關(guān)系,,一個(gè)Interceptor攔截器鏈,一個(gè)ExceptionResolver處理異常,。DispatcherServlet定義如下:

package com.javaeedev.lightweight.mvc;

/**
 * Core dispatcher servlet.
 *
 * @author Xuefeng
 */
public class DispatcherServlet extends HttpServlet {

    private Log log = LogFactory.getLog(getClass());

    private Map<String, ActionAndMethod> actionMap;
    private Interceptor[] interceptors = null;
    private ExceptionResolver exceptionResolver = null;
    private ViewResolver viewResolver = null;

    private Injector injector = null; // Guice IoC容器

    ...
}

Guice的配置完全由Java 5注解完成,,而在DispatcherServlet中,我們需要主動(dòng)從容器中查找某種類型的Bean,,相對(duì)于客戶端被動(dòng)地使用IoC容器(客戶端甚至不能感覺(jué)到IoC容器的存在),,DispatcherServlet需要使用ServiceLocator模式主動(dòng)查找Bean,寫一個(gè)通用方法:

private List<Key<?>> findKeysByType(Injector inj, Class<?> type) {
    Map<Key<?>, Binding<?>> map = inj.getBindings();
    List<Key<?>> keyList = new ArrayList<Key<?>>();
    for(Key<?> key : map.keySet()) {
        Type t = key.getTypeLiteral().getType();
        if(t instanceof Class<?>) {
            Class<?> clazz = (Class<?>) t;
            if(type==null || type.isAssignableFrom(clazz)) {
                keyList.add(key);
            }
        }
    }
    return keyList;
}

DispatcherServlet初始化時(shí)就要首先初始化Guice IoC容器:

public void init(ServletConfig config) throws ServletException {
    String moduleClass = config.getInitParameter("module");
    if(moduleClass==null || moduleClass.trim().equals(""))
        throw new ServletException("Cannot find init parameter in web.xml: <servlet>"
                + "<servlet-name>?</servlet-name><servlet-class>"
                + getClass().getName()
                + "</servlet-class><init-param><param-name>module</param-name><param-value>"
                + "put-your-config-module-full-class-name-here</param-value></init-param></servlet>");
    ServletContext context = config.getServletContext();
    // init guice:
    injector = Guice.createInjector(Stage.PRODUCTION, getConfigModule(moduleClass.trim(), context));
    ...
}

然后,,從IoC容器中查找Action和URL的映射關(guān)系:

private Map<String, ActionAndMethod> getUrlMapping(List<Key<?>> actionKeys) {
    Map<String, ActionAndMethod> urlMapping = new HashMap<String, ActionAndMethod>();
    for(Key<?> key : actionKeys) {
        Object obj = safeInstantiate(key);
        if(obj==null)
            continue;
        Class<Action> actionClass = (Class<Action>) obj.getClass();
        Annotation ann = key.getAnnotation();
        if(ann instanceof Named) {
            Named named = (Named) ann;
            String url = named.value();
            if(url!=null)
                url = url.trim();
            if(!"".equals(url)) {
                log.info("Bind action [" + actionClass.getName() + "] to URL: " + url);
                // link url with this action:
                urlMapping.put(url, new ActionAndMethod(key, actionClass));
            }
            else {
                log.warn("Cannot bind action [" + actionClass.getName() + "] to *EMPTY* URL.");
            }
        }
        else {
            log.warn("Cannot bind action [" + actionClass.getName() + "] because no @Named annotation found in config module. Using: binder.bind(MyAction.class).annotatedWith(Names.named(\"/url\"));");
        }
    }
    return urlMapping;
}

我們假定客戶端是以如下方式配置Action和URL映射的:

public class MyModule implements Module {

    public void configure(Binder binder) {
        // bind actions:
        binder.bind(Action.class)
              .annotatedWith(Names.named("/start.do"))
              .to(StartAction.class);
        binder.bind(Action.class)
              .annotatedWith(Names.named("/register.do"))
              .to(RegisterAction.class);
        binder.bind(Action.class)
              .annotatedWith(Names.named("/signon.do"))
              .to(SignonAction.class);
        ...
    }
}

即通過(guò)Guice提供的一個(gè)注解Names.named()指定URL,。當(dāng)然還可以用其他方法,比如標(biāo)注一個(gè)@Url注解可能更方便,,下一個(gè)版本會(huì)加上,。

Interceptor,ExceptionResolver和ViewResolver也是通過(guò)查找獲得的,。

下面討論DispatcherServlet如何真正處理用戶請(qǐng)求,。第一步是根據(jù)URL查找對(duì)應(yīng)的Action:

String contextPath = request.getContextPath();
String url = request.getRequestURI().substring(contextPath.length());
if(log.isDebugEnabled())
    log.debug("Handle for URL: " + url);
ActionAndMethod am = actionMap.get(url);
if(am==null) {
    response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404 Not Found
    return;
}

沒(méi)找到Action就直接給個(gè)404 Not Found,找到了進(jìn)行下一步,,實(shí)例化一個(gè)Action并填充參數(shù):

// init ActionContext:
HttpSession session = request.getSession();
ServletContext context = session.getServletContext();

ActionContext.setActionContext(request, response, session, context);

// 每次創(chuàng)建一個(gè)新的Action實(shí)例:
Action action = (Action) injector.getInstance(am.getKey());
// 把HttpServletRequest的參數(shù)自動(dòng)綁定到Action的屬性中:
List<String> props = am.getProperties();
for(String prop : props) {
    String value = request.getParameter(prop);
    if(value!=null) {
        am.invokeSetter(action, prop, value);
    }
}

注意,,為了提高速度,所有的set方法已經(jīng)預(yù)先緩存了,,因此避免每次請(qǐng)求都用反射重復(fù)查找Action的set方法,。

然后要應(yīng)用所有的Interceptor以便攔截Action:

InterceptorChainImpl chains = new InterceptorChainImpl(interceptors);
chains.doInterceptor(action);
ModelAndView mv = chains.getModelAndView();

實(shí)現(xiàn)InterceptorChain看上去復(fù)雜,,其實(shí)就是一個(gè)簡(jiǎn)單的遞歸,,大家看InterceptorChainImpl代碼就知道了:

package com.javaeedev.lightweight.mvc;

/**
 * Used for holds an interceptor chain.
 *
 * @author Xuefeng
 */
class InterceptorChainImpl implements InterceptorChain {

    private final Interceptor[] interceptors;
    private int index = 0;
    private ModelAndView mv = null;

    InterceptorChainImpl(Interceptor[] interceptors) {
        this.interceptors = interceptors;
    }

    ModelAndView getModelAndView() {
        return mv;
    }

    public void doInterceptor(Action action) throws Exception {
        if(index==interceptors.length)
            // 所有的Interceptor都執(zhí)行完畢:
            mv = action.execute();
        else {
            // 必須先更新index,再調(diào)用interceptors[index-1],,否則是一個(gè)無(wú)限遞歸:
            index++;
            interceptors[index-1].intercept(action, this);
        }
    }
}

把上面的代碼用try ... catch包起來(lái),,就可以應(yīng)用ExceptionResolver了。

如果得到了ModelAndView,,最后一步就是渲染View了,,這個(gè)過(guò)程極其簡(jiǎn)單:

// render view:
private void render(ModelAndView mv, HttpServletRequest reqest, HttpServletResponse response) throws ServletException, IOException {
    String view = mv.getView();
    if(view.startsWith("redirect:")) {
        // 重定向:
        String redirect = view.substring("redirect:".length());
        response.sendRedirect(redirect);
        return;
    }
    Map<String, Object> model = mv.getModel();
    if(viewResolver!=null)
        viewResolver.resolveView(view, model, reqest, response);
}

最簡(jiǎn)單的JspViewResolver的實(shí)現(xiàn)如下:

package com.javaeedev.lightweight.mvc.view;

/**
 * Let JSP render the model returned by Action.
 *
 * @author Xuefeng
 */
public class JspViewResolver implements ViewResolver {

    /**
     * Init JspViewResolver.
     */
    public void init(ServletContext context) throws ServletException {
    }

    /**
     * Render view using JSP.
     */
    public void resolveView(String view, Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if(model!=null) {
            Set<String> keys = model.keySet();
            for(String key : keys) {
                request.setAttribute(key, model.get(key));
            }
        }
        request.getRequestDispatcher(view).forward(request, response);
    }
}

至此,MVC框架的核心已經(jīng)完成,。

評(píng)
1,。在Action里現(xiàn)在的屬性set支持是基本類型和數(shù)組,但是在一般的應(yīng)用中,,可能是希望直接得到一個(gè)PO,, 這個(gè)怎么實(shí)現(xiàn)
2。一個(gè)action怎么實(shí)現(xiàn)處理多個(gè)請(qǐng)求,,打個(gè)比方,,用戶注冊(cè),,有一個(gè)方法doRegister,還有一個(gè)方法,驗(yàn)證這個(gè)用戶名是否存在,,在前臺(tái)用ajax調(diào)用一次,,doValidateUserName,這兩個(gè)方法都在一個(gè)signupAction里,我在例子和sourcecode里都沒(méi)有找到比較好的解決辦法,,請(qǐng)教樓主,。
3。Example提供的例子非常好,,但是Transaction在action的這個(gè)粒度上interceptor,,感覺(jué)不是很好,從分層的角度講,,不利于封裝,;是不是在business層會(huì)更好一點(diǎn)
xuefeng發(fā)表于07-12-24 09:42
1.得到所有屬性后,就可以在execute()方法中構(gòu)造該Bean,,不想設(shè)計(jì)成Struts的FormBean,,配置特別麻煩

2.寫一個(gè)MultiAction,根據(jù)某個(gè)參數(shù)action=register用反射調(diào)用doRegister()或doValidate()

3.為了演示Interceptor的用法,,和Filter類似,,但是僅限于Action層,不包括Render View,,事務(wù)具體在哪里開(kāi)要根據(jù)具體應(yīng)用確定
bus387發(fā)表于07-12-24 11:19
1.得到所有屬性后,,就可以在execute()方法中構(gòu)造該Bean,不想設(shè)計(jì)成Struts的FormBean,,配置特別麻煩
在execute里再構(gòu)造Bean,當(dāng)然也是可以,,如可以用BeanUtils.setProperties(request, po);  要是框架能做到這些事情,就更完美了,。我相信你一定能做到0配置,,不需要再像Struts一樣有FormBean, 我當(dāng)時(shí)為了0配置到Google搜索就找到這個(gè)框架的。

“2.寫一個(gè)MultiAction,,根據(jù)某個(gè)參數(shù)action=register用反射調(diào)用doRegister()或doValidate()”

Good Idea! 這樣做的話,,doRegister和doValidate需要傳入的參數(shù)和表單提交的數(shù)據(jù)是不一樣的,如果form里有20幾個(gè)參數(shù),,這20幾個(gè)參數(shù)都放在action里作為屬性,,就不好區(qū)分doRegister和doValidate方法需要的了。

xuefeng發(fā)表于07-12-24 13:33
java作為靜態(tài)語(yǔ)言和ruby還是不一樣的,,ruby可以來(lái)個(gè)execute(MyBean bean),,但是java如果這樣搞就必須用反射,而且不能利用接口調(diào)用了,,比較好的方式是定義一個(gè)setBean(MyBean bean),,效果和多個(gè)setBeanProperty1(), setBeanProperty2()效果類似

下個(gè)版本可以考慮在一個(gè)Action中定義多個(gè)executeXxx()方法,,每個(gè)execute方法可以對(duì)應(yīng)一個(gè)setXxx(MyBean bean),url默認(rèn)映射為/baseUrl/xxx?params
freeren發(fā)表于07-12-28 14:53
樓主,,今天一直在研讀你的代碼
然后跟我同事談了這事,,我想問(wèn)下,velocity是不是不再發(fā)布了,,聽(tīng)說(shuō)現(xiàn)地freemarker更多人使用,,是不是這樣?
mdream發(fā)表于07-12-28 14:55
velocity 最近不是已經(jīng)發(fā)了1.5版本了.貌似現(xiàn)在進(jìn)度比以前快很多.APACHE頂級(jí)項(xiàng)目.
xuefeng發(fā)表于07-12-28 15:43
freemarker就是比velocity多一個(gè)格式化功能,,其他都差不多,,主要是velocity配置比較簡(jiǎn)單
freeren發(fā)表于07-12-28 18:20
謝謝兩位的分享,看來(lái)小弟還有很多東西需要學(xué)的,。今天下午小弟測(cè)試了代碼結(jié)果,,tomcat啟動(dòng)時(shí)總是出現(xiàn)這個(gè)錯(cuò)誤:
2007-12-28 17:57:35,781 [main] ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[
/light] - Servlet /light threw load() exception
java.lang.ClassNotFoundException: com.lightmvc.sample.SampleModule

--小弟把目錄改了,在class目錄下也有SampleModule的class文件,,但為什么總是說(shuō)找不到呢,?
freeren發(fā)表于07-12-28 18:23
對(duì)了,還有web.xml的
 <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>*.do</url-pattern>
        <dispatcher>REQUEST</dispatcher> 
    </filter-mapping>
也出現(xiàn)了以下的報(bào)錯(cuò)信息:
Multiple annotations found at this line:
- The content of element type "filter-mapping" must match "(filter-name,
 (url-pattern|servlet-name))".
- Missing end tag "filter-mapping"
小弟把<dispatcher>REQUEST</dispatcher>注釋了就沒(méi)問(wèn)題了,。請(qǐng)問(wèn)這如何解決!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(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)遵守用戶 評(píng)論公約

    類似文章 更多