Latke 源码解析(一)Servlet 部分

本贴最后更新于 2768 天前,其中的信息可能已经物是人非

最近研究 java Web 的 MVC,发现一款轻量级的框架,官网描述为类似 Spring 但以 JSON 为主的 Java Web 框架。具体详情见 latke github。由于此框架的 mvc 部分基于 Servlet 且是对 servlet 的轻量封装,相对 Spring MVC 较为简单,就以此框架来学习 MVC。

官网提供了一个 demo,在 latke-demo github

基于 Servlet

同 Spring MVC 类似,latke 的 web 部分基于 servlet,在 demo 项目中的 web.xml 中找到配置 servlet 的部分如下:

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.b3log.latke.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

可以看到,latke 配置的 servlet 拦截了所有请求,处理请求的类同 Spring MVC 一样,也叫 DispatcherServlet,所以,我们重点看 org.b3log.latke.servlet.DispatcherServlet 这个类。

DispatcherServlet

此类继承了 HttpServlet。并重写了 initservice 方法。我们知道,init 方法是 servlet 的初始化方法,在项目启动时执行。service 是用来处理请求的方法,当有请求到来时执行。先来看 init

init

init 用于初始化 web 的资源和配置,源码如下,这里初始化了与 web 请求相关的几个 handler。对应处理静态资源、注解、请求 url 和参数解析等。具体在 service 分析。

        SYS_HANDLER.add(new StaticResourceHandler(getServletContext()));
        SYS_HANDLER.add(new RequestPrepareHandler());
        SYS_HANDLER.add(new RequestDispatchHandler());
        SYS_HANDLER.add(new ArgsHandler());
        SYS_HANDLER.add(new AdviceHandler());
        SYS_HANDLER.add(new MethodInvokeHandler());

service

真正处理请求的部分就是遍历上述注册的 handler 并依次执行。

 protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
        final HTTPRequestContext httpRequestContext = new HTTPRequestContext();
        httpRequestContext.setRequest(req);
        httpRequestContext.setResponse(resp);
        final HttpControl httpControl = new HttpControl(SYS_HANDLER.iterator(), httpRequestContext);
        try {
            httpControl.nextHandler();  //遍历handler并执行
        } catch (final Exception e) {
            httpRequestContext.setRenderer(new HTTP500Renderer(e));
        }

        result(httpRequestContext);  //处理响应
    }

StaticResourceHandler

第一个注册的 handlar 为 StaticResourceHandler,用于判断是否为静态资源。若是,直接返回,若否,进入下一个 handler 处理。

StaticResourceHandler.handle 部分代码:

  if (StaticResources.isStatic(request)) {  //判断是否静态资源
       if (null == requestDispatcher) {
                //抛出异常,代码就不贴了
       }
      context.setRenderer(new StaticFileRenderer(requestDispatcher));
      return; //返回,直接返回给前台
   }
 httpControl.nextHandler(); //传给下个handler处理

RequestPrepareHandler

请求预处理,目前只是加了个时间戳,略过。

RequestDispatchHandler

这是处理请求路径的 handler,目的是找到与请求路径对应的处理方法(在项目中注解为 @RequestProcessor 等同于 Spring MVC 的 @Controller,@RequestProcessing 等同于 @RequestMapping)。

处理过程:

  1. 在 RequestDispatchHandler 构造器函数中,已将项目所有标注 @RequestProcessing 的方法连同 url 等信息保存于 list 中。
  2. 请求路径(在注解中)与上述 list 进行遍历对比,如发现有对应,就说明找到了处理方法。另外可在请求路径添加类似 restful 格式的路径参数。如 @RequestProcessing(value = "/a/{b}/c") 样式,在方法声明中需写明此参数,类似 void test(String b)
    若没有找到处理请求的参数,直接 404。

需注意:
所有 @RequestProcessor 类已被注册为 bean。这步是 latke 的 Ioc 部分完成的。暂时先不研究。

ArgsHandler

用于处理方法参数。在上一步中得到了处理请求的方法,这里进一步处理其参数。此 handler 最重要的任务是提供了若干参数转化器,按先后顺序,每个参数遍历这些转化器,若匹配成功立即返回。

 registerConverters(new ContextConvert());
 registerConverters(new RequestConvert());
 registerConverters(new ResponseConvert());
 registerConverters(new RendererConvert());
 registerConverters(new JSONObjectConvert());
 registerConverters(new PathVariableConvert());

前 4 个设置了 HTTPRequestContextHttpServletRequestHttpServletResponseRendererConvert 这 4 个类的值,分别对应应用上下文(latke 自建类)、请求、响应、模板。到时可在参数上直接使用这些类。

JSONObjectConvert 是将请求数据转为 json。

PathVariableConvert 匹配除上述参数类型外的其他参数类型。到这里的直接返回匹配成功。再进行相应转化。

AdviceHandler

可以理解类似 AOP 的 handler,主要用于处理 @Before@After 这两个注解。@Before 用于在方法执行前做一些处理,@After 用于在方法后处理。

看一下源码

 public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception {
        // 获取在前面handler得到的匹配方法(result)和参数信息(args)
        final MatchResult result = (MatchResult) httpControl.data(RequestDispatchHandler.MATCH_RESULT);
        @SuppressWarnings("unchecked")
        final Map<String, Object> args = (Map<String, Object>) httpControl.data(ArgsHandler.PREPARE_ARGS);

        final Method invokeHolder = result.getProcessorInfo().getInvokeHolder(); //处理请求的方法
        final Class<?> processorClass = invokeHolder.getDeclaringClass(); //处理请求的类
        final List<AbstractHTTPResponseRenderer> rendererList = result.getRendererList();

        final LatkeBeanManager beanManager = Lifecycle.getBeanManager();
         //获取匹配方法上的@Before信息
        final List<Class<? extends BeforeRequestProcessAdvice>> beforeAdviceClassList = getBeforeList(invokeHolder, processorClass);

        try {
            BeforeRequestProcessAdvice binstance = null;
             //遍历执行@Before类(中的doAdvice方法)
            for (Class<? extends BeforeRequestProcessAdvice> clz : beforeAdviceClassList) {
                binstance = beanManager.getReference(clz);
                binstance.doAdvice(context, args);
            }
        } catch (final RequestReturnAdviceException re) {
           //省略异常处理
        } catch (final RequestProcessAdviceException e) {
           //省略异常处理
        }
        for (AbstractHTTPResponseRenderer renderer : rendererList) {
            renderer.preRender(context, args);
        }
        httpControl.nextHandler();  //执行下一个handler,也就是执行真正匹配的方法体
        
        //下面就是找`@After`注解信息并执行
        for (int j = rendererList.size() - 1; j >= 0; j--) {
            rendererList.get(j).postRender(context, httpControl.data(MethodInvokeHandler.INVOKE_RESULT));
        }

        final List<Class<? extends AfterRequestProcessAdvice>> afterAdviceClassList = getAfterList(invokeHolder, processorClass);
        AfterRequestProcessAdvice instance;

        for (Class<? extends AfterRequestProcessAdvice> clz : afterAdviceClassList) {
            instance = beanManager.getReference(clz);
            instance.doAdvice(context, httpControl.data(MethodInvokeHandler.INVOKE_RESULT));
        }
    }

MethodInvokeHandler

不用多说,这个处理器就是处理真正的方法了。直接看源码吧

  final Method invokeHolder = result.getProcessorInfo().getInvokeHolder();  //得到方法体
  final LatkeBeanManager beanManager = Lifecycle.getBeanManager();
  final Object classHolder = beanManager.getReference(invokeHolder.getDeclaringClass()); //该方法类
  final Object ret = invokeHolder.invoke(classHolder, args.values().toArray()); //执行

这里执行的时机就是 AdviceHandler 中位于执行 @Before@After 中间,也符合逻辑。

返回响应

说完这几个处理器,请求基本是处理完了,下面该返回响应了。让我们回到 DispatcherServlet 中。看看最后的 result 方法。

public static void result(final HTTPRequestContext context) throws IOException {
        final HttpServletResponse response = context.getResponse();
        if (response.isCommitted()) { // Response sends redirect or error
            return;
        }
        AbstractHTTPResponseRenderer renderer = context.getRenderer(); //得到响应模板
        if (null == renderer) {
            renderer = new HTTP404Renderer();
        }
        renderer.render(context);  //返回响应
    }

返回响应后,一次请求就完成了,至此 latke 请求部分大致流程也算说完了,等有时间再谈谈这些实现的细节吧。

-------------end----------------

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 787 关注
  • Servlet
    21 引用 • 29 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • someone756

    厉害了

    1 回复
  • wthfeng
    作者

    哈哈,刚研究。

    1 回复
  • lengjava

    厉害啦我的个

  • tmedivh

    求大神继续分析!!!!

  • xiesguo

    厉害,讲得挺详细的.......

  • monday

    dispatcher 部分有点老了,和源码不一致

    1 回复
  • 88250

    去年做过一次重写,以源码为准 :)

请输入回帖内容 ...