欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

从源码角度了解SpringMVC的执行流程

程序员文章站 2022-10-04 10:52:54
从源码角度了解SpringMVC的执行流程 SpringMVC的执行流程网上有很多帖子都有讲解,流程图和文字描述都很详细,但是你如果没有通过具体源码自己走一遍流程,其实只是死记硬背。所以想开个帖子从源码角度再梳理一遍SpringMVC的执行流程,加深印象。 [TOC] SpringMVC介绍 Spr ......

从源码角度了解springmvc的执行流程

  springmvc的执行流程网上有很多帖子都有讲解,流程图和文字描述都很详细,但是你如果没有通过具体源码自己走一遍流程,其实只是死记硬背。所以想开个帖子从源码角度再梳理一遍springmvc的执行流程,加深印象。

springmvc介绍

  springmvc采用的是前端控制器(front controller) + 各个业务处理器(controller)来处理请求的。前端控制器来响应所有请求,通过一定的调度规则找到具体负责处理的业务处理器,并将请求委派给具体的业务处理器去执行业务逻辑,业务处理器返回给前端控制器模型数据model,最后前端控制器将model交给视图view进行渲染。

从源码角度了解SpringMVC的执行流程

源码分析思路

  看源码的同学可能往往会陷入一个怪圈,刚开始看可能还能看懂,等到一层一层点进去会越来越晕,让自己陷入了太多的细节中,而这些细节其实对主要流程并没有多大影响,然后就埋头研究。之后不得不又从头开始看,又让自己陷入了另一个细节。其实看源码开始时只是需要看一个大致的框架和思路,了解代码的大致执行流程,千万不要让自己陷入到细节的泥潭中。所以本文是通过几个关键的接口作为切入点来梳理springmvc的执行流程,如果我们把关键的接口弄懂了,也就了解了springmvc的执行流程。所以本文只是去了解接口功能,并不关注到具体的实现逻辑上。当我们把大体流程了解后,之后就只是各个击破具体的实现类了。之后作者还会通过自己来实现这些接口来处理自己定义的请求,结合具体的例子来理解。

阅读springmvc源码给我最大的感触有两点:

  • 开放封闭原则,springmvc中可扩展性很强,我们只需要实现具体的接口,然后将接口加入到容器中,就可以实现我们的扩展功能,不需要改任何代码,对扩展开放。而且已有实现类中的关键方法都是用final修饰的,对修改关闭。
  • 面向接口编程,所有重要的流程代码,几乎都是接口调用,而不是具体到某一个特定的类上面。

源码解读

注:源码版本为 spring-webmvc-5.2.2.release.jar

几个关键接口和类

handlermapping

public interface handlermapping {
    @nullable
    handlerexecutionchain gethandler(httpservletrequest request) throws exception;

}

handlermapping映射了请求与具体处理器的关系,可以理解为内存中有这样一个内存数据 map<request, handler>,handlermapping就是根据请求从map中找到handler返回。handlerexecutionchain只是将handler和其对应的拦截器interceptors进行了包装。

前文所说的调度规则,通过请求的 httpservletrequest 获取具体的请求处理类,将请求处理类包装成 handlerexecutionchain 返回。其中 handlerexecutionchain中的object handler就是具体的请求处理类。

public class handlerexecutionchain {
    
    private final object handler;

    @nullable
    private handlerinterceptor[] interceptors;

    @nullable
    private list<handlerinterceptor> interceptorlist;
}

我们现在通过请求找到了具体的处理类,那么我们怎么通过处理类去执行具体的方法呢?那么就需要handleradapter了。

handleradapter

public interface handleradapter {
    
    /**
     * 通过方法 supports 判断适配器是否适配这种类型的handler,返回true则代表适配。
     */
    boolean supports(object handler);
    
    /**
     * 如果适配则通过方法 handle 去让 object handler 执行具体的处理方法。
     */
    @nullable
    modelandview handle(httpservletrequest request, httpservletresponse response
                        , object handler) throws exception;
    
        long getlastmodified(httpservletrequest request, object handler);

}

handleradapter 处理器的适配器,帮助前端控制器去执行处理器handler中具体的处理业务,让前端控制机不需要关注具体的执行细节,也就是说handleradapter对前端控制机屏蔽了处理器执行的具体细节。

modelandview

public class modelandview {

    /** view instance or view name string. */
    @nullable
    private object view;

    /** model map. */
    @nullable
    private modelmap model;

对逻辑视图名view和数据模型的封装。

object view为通常为string类型的逻辑视图名。

modelmap 为mvc中model的角色,底层为map类型。

viewresolver

public interface viewresolver {
    @nullable
    view resolveviewname(string viewname, locale locale) throws exception;

}

解析逻辑视图名viewname找到具体的view,前端控制器找到具体视图view的向导。

view

public interface view {
        void render(@nullable map<string, ?> model, httpservletrequest request
                    , httpservletresponse response) throws exception;

}

调用render方法通过数据模型渲染视图。

请求参数中的 map<string, ?> model 在springmvc中扮演model的角色。

比如我们想返回json格式的数据,那么render方法逻辑就是将model转为json格式输出。

或者我们想返回jsp,那么我们就可以model解析到具体的jsp页面进行展示。

前端控制器 dispatcherservlet

 在springmvc中,dispatcherservlet作为前端控制器,控制服务的具体执行流程,主要的执行流程代码也都在这个类中。

下面是dispatcherservlet简化的源码,只包含了重要的执行流程,保留了关键的执行代码,读者可以具体关键字进行搜索去找到具体的代码行。

public class dispatcherservlet extends frameworkservlet {
    protected void dodispatch(httpservletrequest request, 
                              httpservletresponse response) throws exception {
        handlerexecutionchain mappedhandler = null;
        modelandview mv = null;
        mappedhandler = gethandler(processedrequest);
        handleradapter ha = gethandleradapter(mappedhandler.gethandler());
        mv = ha.handle(processedrequest, response, mappedhandler.gethandler());
        
        render(mv, request, response);
    }
    
    protected void render(modelandview mv, httpservletrequest request
                          , httpservletresponse response) throws exception {
        string viewname = mv.getviewname();
        view = resolveviewname(viewname, mv.getmodelinternal(), locale, request);
        view.render(mv.getmodelinternal(), request, response);
    }
代码流程:
  • 获取处理器:handlermapping通过httpservletrequest请求找到具体的handler
  • 获取处理器对应的适配器:通过handler找到具体的handleradapter
  • 调用处理器的处理逻辑:handleradapter调用handler执行具体的处理逻辑,返回modelandview
  • 解析视图:viewresolver通过modelandview中的逻辑视图名找到具体的view。
  • 渲染视图:view将数据模型进行渲染。
获取处理器
 @nullable
    protected handlerexecutionchain gethandler(httpservletrequest request) throws exception {
        if (this.handlermappings != null) {
            for (handlermapping mapping : this.handlermappings) {
                handlerexecutionchain handler = mapping.gethandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

springmvc在启动的时候会扫描所有实现了handlermapping接口的类,并将这些类加入到容器中。

获取处理器其实就是循环实现了handlermapping的类,调用gethandler()方法,找到了就停止并返回。

每种类型的handler都有各自对应的handlermapping。比如spirngmvc中默认的处理器为 controller,与之对于的handlermapping为requestmappinghandlermapping。

获取处理器的适配器
protected handleradapter gethandleradapter(object handler) throws servletexception {
        if (this.handleradapters != null) {
            for (handleradapter adapter : this.handleradapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
}

逻辑和获取处理器一样,判断逻辑就是前面提过的,只要supports方法返回true,则代表适配这个handler。

同理也是一种handler应该有与之对应的handleradapter。与controller对应的为requestmappinghandleradapter。

所以如果我们要编写自定义的处理器。那么我们需要自己的handler类和与之对于的handlermapping和handleradapter。

解析视图
 @nullable
    protected view resolveviewname(string viewname, @nullable map<string, object> model,
            locale locale, httpservletrequest request) throws exception {

        if (this.viewresolvers != null) {
            for (viewresolver viewresolver : this.viewresolvers) {
                view view = viewresolver.resolveviewname(viewname, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }

仍然是同样的逻辑,所有的代码都是面向接口来开发的。

springmvc支持各种各样的视图渲染,如jsp、json、freemarker、thymeleaf。viewresolver就是这些视图的向导,它告诉springmvc需要通过什么方式去找到具体的视图view。

每种视图view都有自己对应的视图解析器,例如freemarkerview对应的视图解析器为freemarkerviewresolver。

视图渲染

其实就一句代码。

view.render(mv.getmodelinternal(), request, response);

视图渲染,作者刚开始看到这个词,觉得好高大上。其实就是让数据模型model应以什么样的方式来展示。

如果你想将model以json格式返回,那么你就去实现view接口,把model转为json格式,然后写入到响应类的输出流即可。

servletoutputstream out = response.getoutputstream();
baos.writeto(json);
out.flush();

结语

本文只是通过几个重要的接口来描述springmvc的执行流程,没有具体分析实现类的逻辑。也想在这里分享下自己看源码的心得体会。看源码时千万不要让自己陷入过深的业务逻辑中去,先看主要执行流程,重要的接口,比如以debug的方式先预览下执行的方法栈,根据方法栈去定位。如果有哪些地方有误或者有不同的理解,还请不吝赐教。