DispatcherServlet 源码分析

一.DispatcherServlet 源码分析

本文仅了解源码内容即可。

1.观察我们的服务启动⽇志:
当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法. 如果有拦截器, 会先执⾏拦截器 preHandle() ⽅法的代码, 如果 preHandle() 返回true, 继续访问controller中的⽅法. controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() afterCompletion() ,返回给DispatcherServlet, 最终给浏览器响应数据.

1.1初始化(了解) 

DispatcherServlet的初始化⽅法 init() 在其⽗类 HttpServletBean 中实现的
主要作⽤是加载 web.xml 中 DispatcherServlet 的 配置, 并调⽤⼦类的初始化
web.xml是web项⽬的配置⽂件,⼀般的web⼯程都会⽤到web.xml来Listener,Filter,Servlet等, Spring框架从3.1版本开始⽀持Servlet3.0, DispatcherServlet, 实现不再使⽤web.xml

init() 具体代码如下:

 @Override
    public final void init() throws ServletException {
        try {
            // ServletConfigPropertyValues 是静态内部类,使⽤ ServletConfig 获取
            web.xml 中配置的参数
            PropertyValues pvs = new
                    ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            // 使⽤ BeanWrapper 来构造 DispatcherServlet
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new
                    ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new
                    ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        } catch (BeansException ex) {}
        // 让⼦类实现的⽅法,这种在⽗类定义在⼦类实现的⽅式叫做模版⽅法模式
        initServletBean();
    }
HttpServletBean init() 中调⽤了 initServletBean() , 它是在
FrameworkServlet 类中实现的, 主要作⽤是建⽴ WebApplicationContext 容器(有时也称上下⽂), 并加载 SpringMVC 配置⽂件中定义的 Bean到该容器中, 最后将该容器添加到 ServletContext 中
initServletBean() 的具体代码:
    /**
     * Overridden method of {@link HttpServletBean}, invoked after any bean 
     properties
     * have been set. Creates this servlet's WebApplicationContext.
     */
    @Override
    protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring " + getClass().getSimpleName()
                + " '" + getServletName() + "'");
        if (logger.isInfoEnabled()) {
            logger.info("Initializing Servlet '" + getServletName() + "'");
        }
        long startTime = System.currentTimeMillis();
        try {
            //创建ApplicationContext容器
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException | RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            throw ex;
        }
        if (logger.isDebugEnabled()) {
            String value = this.enableLoggingRequestDetails ?
                    "shown which may lead to unsafe logging of potentially sensitive 
            data" :
            "masked to prevent unsafe logging of potentially sensitive data";
            logger.debug("enableLoggingRequestDetails='" +
                    this.enableLoggingRequestDetails +
                    "': request parameters and headers will be " + value);
        }
        if (logger.isInfoEnabled()) {
            logger.info("Completed initialization in " + (System.currentTimeMillis()
                    - startTime) + " ms");
        }
    }
此处打印的⽇志, 也正是控制台打印出来的⽇志:

 源码跟踪技巧:

在阅读框架源码的时候, ⼀定要抓住关键点, 找到核⼼流程.
切忌从头到尾⼀⾏⼀⾏代码去看, ⼀个⽅法的去研究, ⼀定要找到关键流程, 抓住关键点, 先在宏观上对整个流程或者整个原理有⼀个认识, 有精⼒再去研究其中的细节

初始化web容器的过程中, 会通过onRefresh 来初始化SpringMVC的容器 

  protected WebApplicationContext initWebApplicationContext() {
        //...
        if (!this.refreshEventReceived) {
            //初始化Spring MVC
            synchronized (this.onRefreshMonitor) {
                onRefresh(wac);
            }
        }
        return wac;
    }
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy 
     objects.
     */
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context)
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
}
在initStrategies()中进⾏9⼤组件的初始化, 如果没有配置相应的组件,就使⽤默认定义的组件(在
DispatcherServlet.properties中有配置默认的策略, ⼤致了解即可)
⽅法initMultipartResolver、initLocaleResolver、init、initRequestToViewNameTranslator、initFlashMapManager的处理⽅式⼏乎都⼀样(1.2.3.7.8,9),从应⽤⽂中取出指定的Bean, 如果没有, 就使⽤默认的.
⽅法initHandlerMappings、initHandlerAdapters、initHandlerExceptionResolvers的处理⽅式⼏乎
都⼀样(4,5,6)

1.初始化⽂件上传解析器MultipartResolver: 从应⽤上下⽂中获取名称为multipartResolver的Bean,如果没有名为multipartResolver的Bean,则没有提供上传⽂件的解析器
2.初始化区域解析器LocaleResolver: 从应⽤上下⽂中获取名称为localeResolver的Bean,如果没有这个Bean,则默认使⽤AcceptHeaderLocaleResolver作为区域解析器
3.初始化主题解析器ThemeResolver: 从应⽤上下⽂中获取名称为themeResolver的Bean,如果没有这个Bean,则默认使⽤FixedThemeResolver作为主题解析器
4.初始化处理器映射器HandlerMappings: 处理器映射器作⽤,1)通过处理器映射器找到对应的处理器适配器,将请求交给适配器处理;2)缓存每个请求地址URL对应的位置(Controller.xxx⽅法);如果在ApplicationContext发现有HandlerMappings,则从ApplicationContext中获取到所有的HandlerMappings,并进⾏排序;如果在ApplicationContext中没有发现有处理器映射器,则默认BeanNameUrlHandlerMapping作为处理器映射器
5.初始化处理器适配器HandlerAdapter: 作⽤是通过调⽤具体的⽅法来处理具体的请求;如果在 ApplicationContext发现有handlerAdapter,则从ApplicationContext中获取到所有的HandlerAdapter,并进⾏排序;如果在ApplicationContext中没有发现处理器适配器,则默认 SimpleControllerHandlerAdapter作为处理器适配器
6.初始化异常处理器解析器HandlerExceptionResolver: 如果在ApplicationContext发现有
handlerExceptionResolver,则从ApplicationContext中获取到所有的HandlerExceptionResolver,并进⾏排序;如果在ApplicationContext中没有发现异常处理器解析器,则不设置异常处理器

7.初始化RequestToViewNameTranslator: 其作⽤是从Request中获取viewName,从ApplicationContext发现有viewNameTranslator的Bean,如果没有,则默认使⽤DefaultRequestToViewNameTranslator
8.初始化视图解析器ViewResolvers: 先从ApplicationContext中获取名为viewResolver的Bean如果没有,则默认InternalResourceViewResolver作为视图解析器
9.初始化FlashMapManager: 其作⽤是⽤于检索和保存FlashMap(保存从⼀个URL重定向到另⼀个URL时的参数信息),从ApplicationContext发现有flashMapManager的Bean,如果没有,则默认使⽤DefaultFlashMapManager

 1.2 处理请求(核心)

DispatcherServlet 接收到请求后, 执⾏doDispatch 调度⽅法, 再将请求转给Controller.
我们来看doDispatch ⽅法的具体实现:
protected void doDispatch(HttpServletRequest request, HttpServletResponse
            response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;
                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    //1. 获取执⾏链
                    //遍历所有的 HandlerMapping 找到与请求对应的Handler
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    //2. 获取适配器
                    //遍历所有的 HandlerAdapter,找到可以处理该 Handler 的
                    HandlerAdapter
                    HandlerAdapter ha =
                            this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request,
                                mappedHandler.getHandler());
                        if ((new ServletWebRequest(request,
                                response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    //3. 执⾏拦截器preHandle⽅法
                    if (!mappedHandler.applyPreHandle(processedRequest, response))
                    {
                        return;
                    }

                    //4. 执⾏⽬标⽅法
                    mv = ha.handle(processedRequest, response,
                            mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    this.applyDefaultViewName(processedRequest, mv);
                    //5. 执⾏拦截器postHandle⽅法
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler
                            dispatch failed", var21);
                }
                //6. 处理视图, 处理之后执⾏拦截器afterCompletion⽅法
                this.processDispatchResult(processedRequest, response,
                        mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                //7. 执⾏拦截器afterCompletion⽅法
                this.triggerAfterCompletion(processedRequest, response,
                        mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response,
                        mappedHandler, new NestedServletException("Handler processing failed", var23));
            }
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {

                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }
        }
    }
HandlerAdapter 在 Spring MVC 中使⽤了适配器模式:
适配器模式, 也叫包装器模式. 简单来说就是⽬标类不能直接使⽤, 通过⼀个新类进⾏包装⼀下, 适配调⽤⽅使⽤.
把两个不兼容的接⼝通过⼀定的⽅式使之兼容。
HandlerAdapter 主要⽤于⽀持不同类型的处理器(如 Controller、HttpRequestHandler 或者
Servlet 等),让它们能够适配统⼀的请求处理流程。这样,Spring MVC 可以通过⼀个统⼀的接⼝来处理来⾃各种处理器的请求。
从上述源码可以看出在开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle,⽽
applyPreHandle ⽅法的实现源码如下:
 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse
            response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex =
                i++) {
            // 获取项⽬中使⽤的拦截器 HandlerInterceptor
            HandlerInterceptor interceptor =
                    (HandlerInterceptor)this.interceptorList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null);
                return false;
            }
        }
        return true;
    }
在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor , 并执⾏拦截器中的
preHandle ⽅法,这样就会咱们前⾯定义的拦截器对应上了,如下图所⽰:
如果拦截器返回true, 整个发放就返回true, 继续执⾏后。
如果拦截器返回fasle, 则中断后续操作

 

  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringMVC 是 Spring 框架的一部分,主要用于构建 Web 应用程序。它采用了基于请求的模型,通过将请求映射到控制器类和处理方法来处理请求,并使用视图解析器来渲染响应。SpringMVC 的核心是 DispatcherServlet,它充当前端控制器,负责协调处理请求,并调用适当的处理器来处理请求。 下面是 SpringMVC 的源码分析: 1. DispatcherServletDispatcherServlet 是 SpringMVC 的核心类,它充当前端控制器,负责协调处理请求,并调用适当的处理器来处理请求。DispatcherServlet 的主要任务是将请求委托给适当的处理器来处理,然后将处理结果返回给客户端。 2. HandlerMapping:HandlerMapping 是一个接口,用于将请求映射到控制器类和处理方法。SpringMVC 提供了多种 HandlerMapping 实现,例如 BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping 和 RequestMappingHandlerMapping 等。 3. HandlerAdapter:HandlerAdapter 是一个接口,用于将请求委托给适当的处理器来处理。SpringMVC 提供了多种 HandlerAdapter 实现,例如 SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter 和 HttpRequestHandlerAdapter 等。 4. HandlerInterceptor:HandlerInterceptor 是一个接口,用于在控制器执行之前或之后执行一些操作。SpringMVC 提供了多种 HandlerInterceptor 实现,例如 LocaleChangeInterceptor、ThemeChangeInterceptor 和 HandlerInterceptorAdapter 等。 5. ViewResolver:ViewResolver 是一个接口,用于将逻辑视图名称解析为实际的视图对象。SpringMVC 提供了多种 ViewResolver 实现,例如 InternalResourceViewResolver、FreeMarkerViewResolver 和 VelocityViewResolver 等。 6. HandlerExceptionResolver:HandlerExceptionResolver 是一个接口,用于处理控制器抛出的异常。SpringMVC 提供了多种 HandlerExceptionResolver 实现,例如 SimpleMappingExceptionResolver、AnnotationMethodHandlerExceptionResolver 和 DefaultHandlerExceptionResolver 等。 7. ModelAndView:ModelAndView 是一个类,用于封装控制器处理结果和逻辑视图名称。它包含一个模型对象和一个视图名称。 总之,SpringMVC 的源码分析需要深入了解 Spring Framework 的各个方面,包括 IoC 容器、AOP、Servlet API 等。同时,需要对 Web 应用程序的基本概念和架构有一定的了解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值