WebX源码研读

    WebX是公司应用最为广泛的web框架,目前已经开源。一直以为webX是基于spring MVC的,但其实并不是,那么不同之处到底在何处,又是为什么这样实现?看过了源码,在这里梳理下思路
    我以为,在业务层面上来讲web框架解决的核心事情是web请求处理,那么下面就从这个主线出发来看看这个框架是怎么做的:


    这是WebX官网的一张图,我们看到流程中有这么三个关键的节点:WebxFrameWorkFilter , WebxRootController以及WebxController 。然后还有两个关键词,request context 和pipeline  。下面一一从源码来看下它们分别作了什么事情,有啥功能。
1.WebxFrameWorkFilter
    注释是这样写的:“初始化spring容器的filter。”
    那么他本质上是一个servlet的filter,实现了filter接口,我们知道servlet filter 有两个主要方法,init(),doFilter(),init()来做初始化,doFilter()完成当前filter要做的主要事情,下面是doFilter源码
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String path = getResourcePath(request);

        if (isExcluded(path)) {
            log.debug( "Excluded request: {}", path);
            chain.doFilter(request, response);
            return;
        } else {
            log.debug( "Accepted and started to process request: {}", path);
        }

        try {
            getWebxComponents().getWebxRootController().service(request, response, chain);
        } catch (IOException e) {
            throw e;
        } catch (ServletException e) {
            throw e;
        } catch (Exception e) {
            throw new ServletException(e);
        }
 }
    这个方法做了两件事:
    1.检查当前request的path是不是处在排除列表中,就立即放弃控制,将请求控制还给filterchain
    2. 获取已经初始化好的Webx容器,把控制权交给相应的WebxRootController的service方法。注意,webx是有个子容器的概念的,这种划分在业务复杂的时候可以保证良好的业务隔离性。
2.WebxRootController
    WebxRootController是个接口,AbstractWebxRootController对这个接口做了实现,从上面代码来看 WebxFrameWorkFilter 调用了该类的service方法来进一步处理web请求,下面是service的方法源码
 public final void service (HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws Exception {
        RequestContext requestContext = null;

        try {
            requestContext = assertNotNull(getRequestContext(request, response), "could not get requestContext");

            // 如果请求已经结束,则不执行进一步的处理。例如,当requestContext已经被重定向了,则立即结束请求的处理。
            if (isRequestFinished(requestContext)) {
                return;
            }

            // 请求未结束,则继续处理...
            request = requestContext.getRequest();
            response = requestContext.getResponse();

            // 如果是一个内部请求,则执行内部请求
            if (handleInternalRequest(request, response)) {
                return;
            }

            // 如果不是内部的请求,并且没有被passthru,则执行handleRequest
            if (isRequestPassedThru(request) || !handleRequest(requestContext)) {
                // 如果请求被passthru,或者handleRequest返回false(即pipeline放弃请求),
                // 则调用filter chain,将控制交还给servlet engine。
                giveUpControl(requestContext, chain);
            }
        } catch (Throwable e) {
            handleException(requestContext, e);
        } finally {
            commitRequest(requestContext);
        }
    }
    service方法首先会获取当前请求的RequestContext。RequestContext是webX一个比较独特的实现,它封装了HttpServletRequest,HttpServletResponse,以及ServletContext。可以说囊括了当前Http请求的各种状态。
    getRequestContext()方法实现了requestContext获取过程,具体来讲,就是如果发现request attributes 里有没有以key为"_outer_webx3_request_context_" 存储的requestContext对象,如果有就拿过来,没有则创建一个SimpleRequestContext并将其置入request attributes
    获取到requestContext后,如果不是内部请求或者不需要进一步处理的请求,接下来就走到handleRequest方法,这个方法默认实现如下:
 @Override
    protected boolean handleRequest (RequestContext requestContext) throws Exception {
        HttpServletRequest request = requestContext.getRequest();

        // Servlet mapping有两种匹配方式:前缀匹配和后缀匹配。
        // 对于前缀匹配,例如:/servlet/aaa/bbb,servlet path为/servlet,path info为/aaa/bbb
        // 对于前缀匹配,当mapping pattern为/*时,/aaa/bbb,servlet path为"",path info为/aaa/bbb
        // 对于后缀匹配,例如:/aaa/bbb.html,servlet path为/aaa/bbb.html,path info为null
        //
        // 对于前缀匹配,取其pathInfo;对于后缀匹配,取其servletPath。
        String path = ServletUtil. getResourcePath(request);

        // 再根据path查找component
        WebxComponent component = getComponents().findMatchedComponent(path);
        boolean served = false;

        if (component != null) {
            try {
                WebxUtil. setCurrentComponent(request, component);
                served = component.getWebxController().service(requestContext);
            } finally {
                WebxUtil. setCurrentComponent(request, null);
            }
        }

        return served;
    }
    可以看到,根据path路由到了对应的子容器,并触发WebxController的service方法
3.WebxController
    直接来看service方法
 public boolean service(RequestContext requestContext) throws Exception {
        PipelineInvocationHandle handle = pipeline.newInvocation();

        handle.invoke ();

        // 假如pipeline被中断,则视作请求未被处理。filter将转入chain中继续处理请求。
        return !handle.isBroken();
 }
 
    这里看到最终是执行到pipeline。pipeLine是一组顺序执行操作的抽象接口,这里应该是借鉴了tomcat 和struts的实现方法,是一种典型的责任链设计模式。pipline包含了一个valve的概念,valve在源代码里注释说是“如同真实世界的水管中的阀门,可以控制液体流向,也可以控制pipline里后续valves的执行
    代码中PipelineInvocationHandle 实际上保存了当前pipline的状态,触发的invoke方法实际上是pipleImpl里的invokeNext(),这个方法最终触发不同的valve调用invoke()方法,可见,valve是webx整个流程的金字塔底部,处理不同请求的worker。valve的各种实现包括渲染页面、登陆认证、异常处理等等,构建了webx处理请求的基石。
 
    通过上述分析,总体感觉WebX对模块化执行的比较彻底,甚至可以允许产生不同的子容器,每个子容器有不同的配置。requestContext介绍说是对request、response和servletContext进行了封装,但是从上面的请求过程来看,只是对request进行了包装(或者其他地方我还没看到)。pipline是一个很经典的设计,让整个web请求处理过程更加内聚化,流程也比较清晰。但是同时也可能让这个流程比较重。
    相信webx这些实践是公司网站发展过程中解决所遇到问题的一个框架上的反映。换句话说,按照公司目前的网站规模与稳定性要求,它应该比较适合于大型网站,因为整个框架还是显得比较重的。
 

猜你喜欢

转载自jokert.iteye.com/blog/1908575
今日推荐