Argo源码阅读(六):ArgoDispatcher

来源:互联网 发布:朵朵淘宝小号批发 编辑:程序博客网 时间:2024/04/28 05:54

回到最顶层的调用ArgoFilter.init(),Argo.init()初始化了ArgoDispatcher,ArgoFilter初始化完毕。

 

ArgoFilter.doFilter实现过滤功能

    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        HttpServletRequest httpReq = (HttpServletRequest) request;        HttpServletResponse httpResp = (HttpServletResponse) response;        dispatcher.service(httpReq, httpResp);    }

只是简单地交给ArgoDispatcher处理。


接口ArgoDispatcher添加了@ImplementedBy(DefaultArgoDispatcher.class)注解,@ImplementedBy注解设置默认的实现类和bind(ArgoDispatcher.class).to(DefaultArgoDispatcher.class)的结果是等价的。


下面介绍DefaultArgoDispatcher


    @Inject    public DefaultArgoDispatcher(Argo argo, Router router, StatusCodeActionResult statusCodeActionResult, MultipartConfigElement config) {        this.argo = argo;        this.router = router;        this.statusCodeActionResult = statusCodeActionResult;        this.config = config;        this.logger = argo.getLogger(this.getClass());        logger.info("constructed.", this.getClass());}

可以发现构造函数添加了@Inject注解,参数是通过注入实现实例化的。

    @Override     protected void configure() {…        bind(MultipartConfigElement.class)                .toProvider(DefaultMultipartConfigElementProvider.class)                .in(Singleton.class);…    }

Argo.init()中,ArgoDispatcher是在buildInjector之后构造的,构造注解器的参数Module中有ArgoModule。ArgoModule中对DefaultArgoDispatcher的接口和实现类进行了bind。


DefaultArgoDispatcher的service方法

    public void service(HttpServletRequest request, HttpServletResponse response) {        ArgoRequest argoRequest = new ArgoRequest(request, config);        try {            BeatContext beatContext = bindBeatContext(argoRequest, response);            route(beatContext);        } finally {            Closeables.closeQuietly(argoRequest);        }    }


bindBeatContext:
    private BeatContext bindBeatContext(HttpServletRequest request, HttpServletResponse response) {        Context context = new Context(request, response);        localContext.set(context);        BeatContext beat = argo.injector().getInstance(defaultBeatContextKey);        // 增加默认参数到model        beat.getModel().add("__beat", beat);        context.setBeat(beat);        return beat;    }


根据defaultBeatContexKey找到符合的匹配是DefaultBeatContext,因为在ArgoModule.configure()发现BeatContext绑定了

    DefaultBeatContext        bind(BeatContext.class)               .annotatedWith(ArgoSystem.class)               .to(DefaultBeatContext.class);

实现了BeatContext接口的有两个类,BeatContextWrapper和DefaultBeatContext。BeatContextWrapper比较简单,DefaultBeatContext则通过注解加入了其它部分。

@ArgoSystempublic class DefaultBeatContext implements BeatContext {    @Inject    public DefaultBeatContext(HttpServletRequest request, HttpServletResponse response, Model model, ClientContext clientContext, ServletContext servletContext) {        this.request = request;        this.response = response;        this.model = model;        this.clientContext = clientContext;        this.servletContext = servletContext;    }}

Route():

Router接口添加了@ImplementedBy注解

@ImplementedBy(DefaultRouter.class)public interface Router {    public ActionResult route(BeatContext beat);}


//DefaultArgoDispatcher.route()    private void route(BeatContext beat) {        try {            ActionResult result = router.route(beat);            if (ActionResult.NULL == result)                result = statusCodeActionResult.getSc404();            result.render(beat);        } catch (Exception e) {            statusCodeActionResult.render405(beat);        } finally {            localContext.remove();        }    }

DefaultRouter构造时将会初始化一个actions变量
    @Inject    public DefaultRouter(Argo argo, @ArgoSystem Set<Class<? extends ArgoController>> controllerClasses, @StaticActionAnnotation Action staticAction) {        this.argo = argo;        argo.getLogger().info("initializing a %s(implements Router)", this.getClass());        this.actions = buildActions(argo, controllerClasses, staticAction);        argo.getLogger().info("%s(implements Router) constructed.", this.getClass());    }

在buildActions中将会对所有的Controller通过ControllerInfo调用analyze
    List<Action> buildActions(Set<ArgoController> controllers, Action staticAction) {    ...        for (ArgoController controller : controllers) {            ControllerInfo controllerInfo = new ControllerInfo(controller);            List<ActionInfo> subActions = controllerInfo.analyze();            ...        }    }

analyze会找到所有符合条件的Method(返回类型是ActionResult的public方法),然后构造ActionInfo。ActionInfo构造是会找到方法的前置/后置拦截器,参数类型,参数名,确定GET/POST类型,计算order。

DefaultRouter构造完之后,在route()方法中会用到根据order排序好的Actions。对actions按序执行matchAndInvoke。

MethodAction的matchAndInvoke会检查是否Http请求,用AntPathMatcher检查路径匹配,然后执行拦截器,调用实际的方法。
    public RouteResult matchAndInvoke(RouteBag bag) {        if (!actionInfo.matchHttpMethod(bag))            return RouteResult.unMatch();        Map<String, String> uriTemplateVariables = Maps.newHashMap();        boolean match = actionInfo.match(bag, uriTemplateVariables);        if (!match)            return RouteResult.unMatch();        // PreIntercept        for(PreInterceptor preInterceptor : actionInfo.getPreInterceptors()) {            ActionResult actionResult = preInterceptor.preExecute(bag.getBeat());            if (ActionResult.NULL != actionResult)                return RouteResult.invoked(actionResult);        }        ActionResult actionResult = actionInfo.invoke(uriTemplateVariables);        // PostIntercept        for(PostInterceptor postInterceptor : actionInfo.getPostInterceptors()) {            actionResult = postInterceptor.postExecute(bag.getBeat(), actionResult);        }        return RouteResult.invoked(actionResult);    }

preExecute如果返回NULL则直接显示,否则执行后续的interceptor。

 

Action.invoke则调用对应的Controller中匹配路径的方法。

render()

渲染页面,实现ActionResult接口的比较多。404,405,redirect,redirect301,VelocityViewResult,InnerPrintWriter,DefaultStaticResult。

 

根据返回的ActionResult类型执行对应的render方法渲染页面


至此Argo的基本流程也就走过一遍了
原创粉丝点击