Latke web应用框架Dispatcher类代码解读

简介

Latke('l ɑ:tk ə,土豆饼)是一个简单易用的 Java Web 应用开发框架,包含 MVC、IoC/AOP、事件通知、ORM、插件等组件。

Dispatcher 是 MVC 的核心的一个类

        final Dispatcher.RouterGroup routeGroup = Dispatcher.group();
        routeGroup.get("/", helloProcessor::index).
                get("/register", registerProcessor::showRegister).
                post("/register", registerProcessor::register).
                get("/var/{pathVar}", registerProcessor::paraPathVar).
                router().get().post().uri("/greeting").handler(helloProcessor::greeting);
        Dispatcher.mapping();

通过 group 方法获取到 RouterGroup 后可以快速指定 url 路径对应的处理方法

代码解读

Dispatcher 中有两个比较重要的内部类,Router 和 RouterGroup

Router

Router 用于记录 uri 及方法信息,通过 get()、uri()等方法设置相关信息

重点来看 handler 方法

public Router handler(final Handler handler) {
            this.handler = handler;
            final Class<?> clazz = handler.getClass();
            try {
                final Serializable lambda = handler;
                // Latke 框架中 "writeReplace" 是个什么魔数? https://ld246.com/article/1568102022352
                final Method m = clazz.getDeclaredMethod("writeReplace");
                m.setAccessible(true);
                final SerializedLambda sl = (SerializedLambda) m.invoke(lambda);
                final String implClassName = sl.getImplClass().replaceAll("/", ".");
                final Class<?> implClass = Class.forName(implClassName);
                this.method = implClass.getDeclaredMethod(sl.getImplMethodName(), RequestContext.class);
            } catch (final Exception e) {
                LOGGER.log(Level.ERROR, "Found lambda method reference impl method failed", e);
            }

            return this;
        }

在此之前,我们先把目光转移到 handler 方法的参数上

@FunctionalInterface
public interface Handler extends Serializable {

    /**
     * Performs request handling with the specified context.
     *
     * @param context the specified context
     */
    void handle(final RequestContext context);
}

Handler是一个使用了@FunctionalInterface并继承了Serializable的接口类

既然使用了@FunctionalInterface注解,表明这是一个函数式接口,那就和Lambda表达式脱不了关系

我们很难直接从Handler对象中获取到开发者传入的方法信息,上面说到了Lambda表达式和继承了Serializable,在源码中出现了一个魔数writeReplace,在继续讲之前我们需要补充一点关于序列化的知识

1.当我们为某一个类实现了Serializable接口。可以进行序列化和反序列化操作。

2.当进行序列化操作时:(将对象写入到磁盘中)

会先调用其writeReplace方法。

然后会调用其writeObject方法。
3.当调用其writeReplace方法时,会返回一个SerializedLambda对象

Serialized form of a lambda expression. The properties of this class represent the information that is present at the lambda factory site, including static metafactory arguments such as the identity of the primary functional interface method and the identity of the implementation method, as well as dynamic metafactory arguments such as values captured from the lexical scope at the time of lambda capture.

Implementors of serializable lambdas, such as compilers or language runtime libraries, are expected to ensure that instances deserialize properly. One means to do so is to ensure that the writeReplacemethod returns an instance of SerializedLambda, rather than allowing default serialization to proceed.

SerializedLambda has a readResolve method that looks for a (possibly private) static method called $deserializeLambda$(SerializedLambda) in the capturing class, invokes that with itself as the first argument, and returns the result. Lambda classes implementing $deserializeLambda$ are responsible for validating that the properties of the SerializedLambda are consistent with a lambda actually captured by that class.

翻译: lambda 表达式的序列化形式。 此类的属性表示存在于 lambda factory site的信息,包括静态metafactory参数,例如主要函数式接口方法的标识和实现方法的标识,以及动态metafactory 参数,如在lambda捕获时从词法作用域捕获的值。

可序列化 lambda 的实现者(例如编译器或语言运行时库)应确保实例正确反序列化。 这样做的一种方法是确保 writeReplace 方法返回 SerializedLambda 的实例,而不是允许进行默认的序列化。

SerializedLambda 有一个 readResolve 方法,该方法在捕获类中查找名为 $deserializeLambda$(SerializedLambda) 的(可能是私有的)静态方法,将其自身作为第一个参数调用,并返回结果。 实现 $deserializeLambda$ 的 Lambda 类负责验证 SerializedLambda 的属性是否与该类实际捕获的 lambda 一致。

这是官方对SerializedLambda的描述,可以看出其中包含了函数式接口的相关信息,我们拿到SerializedLambda对象后就可以获取到开发者传入的方法信息,然后我们再将class及method信息记录即可,在需要的时候调用方法即可。

RouterGroup

RouterGroup职责比较简单,通过一系列的方法,记录Router及middleware(中间件handler)的信息列表

Dispatcher

再回到Dispatcher本身,Dispatcher中有三个成员变量:HANDLERS,startRequestHandler,endRequestHandler

    static {
        HANDLERS.add(new StaticResourceHandler());
        HANDLERS.add(new RouteHandler());
        HANDLERS.add(new InvokeHandler());
    }

HANDLERS会静态初始化三个Handler

StaticResourceHandler

public class StaticResourceHandler implements Handler {

    @Override
    public void handle(final RequestContext context) {
        final Request request = context.getRequest();
        if (request.isStaticResource()) {
            context.setRenderer(new StaticFileRenderer());
            context.abort();
            return;
        }

        context.handle();
    }
}

从代码中可以看出,会通过request判断是否是静态资源,会设置Renderer为静态文件的Renderer,等后续的文章再继续解读Renderer

同时会调用abort()方法,用于打断后续继续执行handler

    public void abort() {
        handleIndex = 64;
    }

abort方法会将handleIndex设置为64,那注定了最大的handlers的长度不能超过64,否则就会导致bug

RouteHandler

public class RouteHandler implements Handler {

    @Override
    public void handle(final RequestContext context) {
        final Request request = context.getRequest();

        final long startTimeMillis = System.currentTimeMillis();
        request.setAttribute(Keys.HttpRequest.START_TIME_MILLIS, startTimeMillis);
        String requestURI = getRequestURI(request);
        requestURI = StringUtils.substringAfter(requestURI, Latkes.getContextPath()); // remove context path
        final String httpMethod = getHttpMethod(request);
        LOGGER.log(Level.DEBUG, "Request [requestURI={}, method={}]", requestURI, httpMethod);

        final RouteResolution result = doMatch(requestURI, httpMethod);
        if (null == result) {
            context.abort();
            return;
        }

        // 插入中间件
        final ContextHandlerMeta contextHandlerMeta = result.getContextHandlerMeta();
        final List<Handler> middlewares = contextHandlerMeta.getMiddlewares();
        for (int i = middlewares.size() - 1; 0 <= i; i--) {
            final Handler middleware = middlewares.get(i);
            context.insertHandlerAfter(middleware);
        }

        context.pathVars(result.getPathVars());
        context.attr(RequestContext.MATCH_RESULT, result);
        context.handle();
    }

	/**
	* 匹配路由对应的处理方法
	*/
    public static RouteResolution doMatch(final String requestURI, final String httpMethod) {
		//....
	}

    public static void addContextHandlerMeta(final ContextHandlerMeta contextHandlerMeta) {
		//....
	}

    private static RouteResolution route(final String requestURI, final String method, final Map<String, ContextHandlerMeta> pathVarContextHandlerMetasHolder) {
		//....
	}
	//....
}

RoutherHandler的职责主要是进行路由匹配,从request对象获取到URI后调用doMatch方法匹配出对应的RouteResoultion,RouteResoultion记录了对应的方法信息等。

InvokeHandler

    @Override
    public void handle(final RequestContext context) {
        final RouteResolution result = (RouteResolution) context.attr(RequestContext.MATCH_RESULT);
        final ContextHandlerMeta contextHandlerMeta = result.getContextHandlerMeta();
        final Method invokeHolder = contextHandlerMeta.getInvokeHolder();
        final BeanManager beanManager = BeanManager.getInstance();
        final Object classHolder = beanManager.getReference(invokeHolder.getDeclaringClass());
        try {
            invokeHolder.invoke(classHolder, context);
        } catch (final Throwable e) {
            LOGGER.log(Level.ERROR, "Handler processing failed: ", e);
            context.sendError(500);
            context.abort();
            return;
        }

        context.handle();
    }

Latke包含IOC控制反转的组件,所有在进行路由注册的时候对应的Processor类都有@Singleton注解,交由给Latke进行管理,在InvokeHandler里由RouteHandler获取到的RouteResolution中解析出ContextHandlerMeta然后使用BeanManager获取到对应的单例Bean类,最后使用反射进行方法的调用

Dispatcher handle()

再看回Dispatcher方法的handle方法,这里能看出来整个handler的调用方式和过程

    public static RequestContext handle(final Request request, final Response response) {
        final RequestContext ret = new RequestContext(request, response);
        response.context = request.context = ret;
        ret.addHandlers(HANDLERS);

        if (null != startRequestHandler) {
            try {
                startRequestHandler.handle(ret);
            } catch (final Exception e) {
                LOGGER.log(Level.ERROR, "Start request handle failed", e);
            }
        }

        ret.handle();
        renderResponse(ret);

        if (null != endRequestHandler) {
            try {
                endRequestHandler.handle(ret);
            } catch (final Exception e) {
                LOGGER.log(Level.ERROR, "End request handle failed", e);
            }
        }

        return ret;
    }

首先会将已经配置好的HANDLERS放入到context中的handlers中,然后判断Dispatcher中的startRequestHandler是否能调用,这个发生在所有handler调用之前

之后会调用RequestContext的handle方法

    public void handle() {
        try {
            for (handleIndex++; handleIndex < handlers.size(); handleIndex++) {
                handlers.get(handleIndex).handle(this);
            }
        } catch (final Exception e) {
            final String requestLog = Requests.getLog(request);
            LOGGER.log(Level.ERROR, "Handler process failed: " + requestLog, e);
            setRenderer(new Http500Renderer(e));
        }
    }

这里表明了整个handlers列表的调用方式,类似于链式调用的方式,由handleIndex来进行定位该调用哪一个handle,所以上文提到的abort方法中的方式会导致最长的handlers的长度为64,否则会导致在handle中继续调用index > 64 的handler

总结

Dispatcher主要实现了包括定义路由规则,记录请求处理的整个链路的过程,是整个MVC的一个比较和核心的部分,告诉了请求进来后该怎么走已经该进行什么样的操作并调用开发者定义好的业务处理方法。

参考

Latke 快速上手指南 - 链滴 (ld246.com)

JAVA序列化与反序列化内容_java中gson中writereplace方法-CSDN博客

Java8的SerializedLambda详解-CSDN博客

我的知识小屋

Fetter的知识小屋

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值