通过 Latke-Demo 对 Latke 工作流程的初步分析

本贴最后更新于 3046 天前,其中的信息可能已经时移世异

Latke 代码看到 IOC 包后就彻底懵逼了,从已看过的几段代码来看,都是各种 set、get,变量名称含义抽象,不知所云。由此感到单纯的读代码恐怕无法理解 IOC 包的实现逻辑了。于是决定运行一下 demo,来跟踪一下代码。以下是 latke-demo 项目从请求发起到响应结束时经过的代码段(层级关系可能不太正确,但是大致流程就是如此)
虽然还没有完全明白,但是跟踪下来感觉颇有收获,待慢慢补充细节

1. DispatcherServlet.service

//以此作为起点。此处是最原始的servlet,配置在web.xml中,再细节的方面,暂时可以当做黑盒了。 //不得不说,jsp+servlet作为基础,当年没有好好学是个错误。(垃圾讲师误人啊……) protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { //HTTPRequestContext,Latke封装,没有定义构造器 final HTTPRequestContext httpRequestContext = new HTTPRequestContext(); httpRequestContext.setRequest(req); httpRequestContext.setResponse(resp); /* * 在servlet的init方法中,为SYS_HANDLE:List<Handler>添加了如下Handler * StaticResourceHandler(getServletContext())); * RequestPrepareHandler()); * RequestDispatchHandler()); * ArgsHandler()); * AdviceHandler()); * MethodInvokeHandler()); */ final HttpControl httpControl = new HttpControl(SYS_HANDLER.iterator(), httpRequestContext); try { //【1】在此处转而进行上述列表中的handler方法 //这样看标题2、3的过程就一目了然了 httpControl.nextHandler(); } catch (final Exception e) { httpRequestContext.setRenderer(new HTTP500Renderer(e)); } result(httpRequestContext); }

2. HttpControl.nextHandler

public void nextHandler() { if (ihandlerIterable.hasNext()) { try { ihandlerIterable.next().handle(httpRequestContext, this); } catch (final Exception e) { LOGGER.log(Level.ERROR, "Request processing failed", e); } } }

3. handle

3.1 StaticResourceHandler.handle

public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception { final HttpServletRequest request = context.getRequest(); if (StaticResources.isStatic(request)) { if (null == requestDispatcher) { throw new IllegalStateException( "A RequestDispatcher could not be located for the default servlet [" + this.defaultServletName + "]"); } context.setRenderer(new StaticFileRenderer(requestDispatcher)); return; } httpControl.nextHandler(); }

3.2 RequestPrepareHandler

public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception { final HttpServletRequest request = context.getRequest(); final long startTimeMillis = System.currentTimeMillis(); request.setAttribute(Keys.HttpRequest.START_TIME_MILLIS, startTimeMillis); httpControl.nextHandler(); }

3.3 RequestDispatchHandler

public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception { final HttpServletRequest request = context.getRequest(); final String requestURI = getRequestURI(request); final String httpMethod = getHTTPMethod(request); final MatchResult result = doMatch(requestURI, httpMethod); if (result != null) { httpControl.data(MATCH_RESULT, result); httpControl.nextHandler(); } }

3.4 ArgsHandler

public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception { final MatchResult result = (MatchResult) httpControl.data(RequestDispatchHandler.MATCH_RESULT); final Method invokeHolder = result.getProcessorInfo().getInvokeHolder(); final Map<String, Object> args = new LinkedHashMap<String, Object>(); final Class<?>[] parameterTypes = invokeHolder.getParameterTypes(); final String[] paramterNames = getParamterNames(invokeHolder); for (int i = 0; i < parameterTypes.length; i++) { doParamter(args, parameterTypes[i], paramterNames[i], context, result, i); } httpControl.data(PREPARE_ARGS, args); httpControl.nextHandler(); }

3.5 AdviceHandler

【AskD】这个 Handler 字面上不太理解

public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception { 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(); final List<Class<? extends BeforeRequestProcessAdvice>> beforeAdviceClassList = getBeforeList(invokeHolder, processorClass); try { BeforeRequestProcessAdvice binstance = null; for (Class<? extends BeforeRequestProcessAdvice> clz : beforeAdviceClassList) { binstance = beanManager.getReference(clz); binstance.doAdvice(context, args); } } catch (final RequestReturnAdviceException re) { return; } catch (final RequestProcessAdviceException e) { final JSONObject exception = e.getJsonObject(); final String msg = exception.optString(Keys.MSG); final int statusCode = exception.optInt(Keys.STATUS_CODE, -1); if (-1 != statusCode && HttpServletResponse.SC_OK != statusCode) { final HttpServletResponse response = context.getResponse(); response.sendError(statusCode, msg); } else { final JSONRenderer ret = new JSONRenderer(); ret.setJSONObject(exception); context.setRenderer(ret); } return; } for (AbstractHTTPResponseRenderer renderer : rendererList) { renderer.preRender(context, args); } //【2】 => 3.6 httpControl.nextHandler(); 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)); } }

3.6 MethodInvokeHandler

public void handle(final HTTPRequestContext context, final HttpControl httpControl) throws Exception { final MatchResult result = (MatchResult) httpControl.data(RequestDispatchHandler.MATCH_RESULT); final Map<String, Object> args = (Map<String, Object>) httpControl.data(ArgsHandler.PREPARE_ARGS); // get class instance 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()); httpControl.data(INVOKE_RESULT, ret); }

【AskD】:总觉得这种处理流程有点奇怪,为什么不是在标题 2 的 nexthandler 中循环调用,而是用这种链式引用呢?

4. JavassistMethodHandler.invoke

执行到这一步,参数 method 中已经有了相应 processor 路径,未知是从哪里赋值的,需要再跟

public Object invoke(final Object proxy, final Method method, final Method proceed, final Object[] params) throws Throwable { //反射,获得处理类的类名 final Class<?> declaringClass = method.getDeclaringClass(); //获取处理方法名:ClassName.MethodName final String invokingMehtodName = declaringClass.getName() + '#' + method.getName(); // 1. @BeforeMethod handle handleInterceptor(invokingMehtodName, params, BeforeMethod.class); // 2. Invocation with transaction handle //【AskD】这里看起来很突兀,为何会用到userRepository? final UserRepository userRepository = beanManager.getReference(UserRepository.class); final boolean withTransactionalAnno = method.isAnnotationPresent(Transactional.class); final boolean alreadyInTransaction = userRepository.hasTransactionBegun(); final boolean needHandleTrans = withTransactionalAnno && !alreadyInTransaction; // Transaction Propagation: REQUIRED (Support a current transaction, create a new one if none exists) Transaction transaction = null; if (needHandleTrans) { transaction = userRepository.beginTransaction(); } Object ret = null; try { ret = proceed.invoke(proxy, params); if (needHandleTrans) { transaction.commit(); } } catch (final InvocationTargetException e) { if (needHandleTrans) { if (null != transaction && transaction.isActive()) { transaction.rollback(); } } throw e.getTargetException(); } // 3. @AfterMethod handle handleInterceptor(invokingMehtodName, params, AfterMethod.class); return ret; }

4.1 JavassistMethodHandler.handleInterceptor

private void handleInterceptor(final String invokingMehtodName, final Object[] params, final Class<? extends Annotation> interceptAnnClass) { final Set<Interceptor> interceptors = InterceptorHolder.getInterceptors(invokingMehtodName, interceptAnnClass); for (final Interceptor interceptor : interceptors) { final Method interceptMethod = interceptor.getInterceptMethod(); final Class<?> interceptMethodClass = interceptMethod.getDeclaringClass(); try { final Object reference = beanManager.getReference(interceptMethodClass); interceptMethod.invoke(reference, params); } catch (final Exception e) { final String errMsg = "Interception[" + interceptor.toString() + "] execute failed"; LOGGER.log(Level.ERROR, errMsg, e); throw new RuntimeException(errMsg); } } }
4.1.1 InterceptorHolder.getInterceptors
static Set<Interceptor> getInterceptors(final String invokingMethodName, final Class<? extends Annotation> interceptAnnClass) { if (BeforeMethod.class.equals(interceptAnnClass)) { final Set<Interceptor> ret = BEFORE_METHOD_HOLDER.get(invokingMethodName); if (null == ret) { return Collections.emptySet(); } return ret; } else if (AfterMethod.class.equals(interceptAnnClass)) { final Set<Interceptor> ret = AFTER_METHOD_HOLDER.get(invokingMethodName); if (null == ret) { return Collections.emptySet(); } return ret; } return Collections.emptySet(); }

5. HelloProcessor.index

public void index(final HTTPRequestContext context) { final AbstractFreeMarkerRenderer render = new FreeMarkerRenderer(); context.setRenderer(render); render.setTemplateName("index.ftl"); final Map<String, Object> dataModel = render.getDataModel(); dataModel.put("greeting", "Hello, Latke!"); }

6. MethodInvokeHandler.handle

//这里并不是真的返回到了这个方法 //而是栈的弹出过程(几乎用到了上述列举的所有Handler) //httpControl.nextHandler是一个递归调用的过程? //或许只是因为调试调用栈的问题看起来像递归,看方法名应该是链表遍历才对…

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

8. AbstractFreeMarkerRenderer.render

public void render(final HTTPRequestContext context) { final HttpServletResponse response = context.getResponse(); response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); PrintWriter writer; try { writer = response.getWriter(); } catch (final Exception e) { try { writer = new PrintWriter(response.getOutputStream()); } catch (final IOException ex) { LOGGER.log(Level.ERROR, "Can not get response writer", ex); return; } } if (response.isCommitted()) { // response has been sent redirect writer.flush(); writer.close(); return; } final HttpServletRequest request = context.getRequest(); final Template template = getTemplate((String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME), templateName); if (null == template) { LOGGER.log(Level.ERROR, "Not found template[{0}]", templateName); try { response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (final IOException ex) { LOGGER.log(Level.ERROR, "Can not send error 404!", ex); } return; } try { dataModel.put(Keys.REQUEST, request); Keys.fillServer(dataModel); //这里好像什么都没有 beforeRender(context); final String html = genHTML(context.getRequest(), dataModel, template); doRender(html, context.getRequest(), response); afterRender(context); } catch (final Exception e) { LOGGER.log(Level.ERROR, "FreeMarker renders error", e); try { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } catch (final IOException ex) { LOGGER.log(Level.ERROR, "Can not send error 500!", ex); } } }

8.1 AbstractFreeMarkerRenderer.genHTML

protected String genHTML(final HttpServletRequest request, final Map<String, Object> dataModel, final Template template) throws Exception { final StringWriter stringWriter = new StringWriter(); template.setOutputEncoding("UTF-8"); template.process(dataModel, stringWriter); final StringBuilder pageContentBuilder = new StringBuilder(stringWriter.toString()); final long endimeMillis = System.currentTimeMillis(); final String dateString = DateFormatUtils.format(endimeMillis, "yyyy/MM/dd HH:mm:ss"); final long startTimeMillis = (Long) request.getAttribute(Keys.HttpRequest.START_TIME_MILLIS); final String msg = String.format("<!-- Generated by B3log Latke(%1$d ms), %2$s -->", endimeMillis - startTimeMillis, dateString); pageContentBuilder.append(msg); return pageContentBuilder.toString(); }

8.2 AbstractFreeMarkerRenderer.doRender

protected void doRender(final String html, final HttpServletRequest request, final HttpServletResponse response) throws Exception { PrintWriter writer; try { writer = response.getWriter(); } catch (final Exception e) { writer = new PrintWriter(response.getOutputStream()); } if (response.isCommitted()) { // response has been sent redirect writer.flush(); writer.close(); return; } writer.write(html); writer.flush(); writer.close(); }
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 165 关注
  • CodeView
    5 引用 • 5 回帖
  • Latke

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

    71 引用 • 535 回帖 • 820 关注

相关帖子

欢迎来到这里!

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

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