《Spring专题》-spring源码解析(六)Spring mvc的工作原理和机制

前文:这一篇幅我们主要来讲解一下Spring Mvc的请求流程,看看Spring框架是如何和Servlet进行整合,然后进行处理请求的。按照惯例,我们还是来说明一下重要的接口和类。

一、本篇幅解析源码用到的接口和类。

1、DispatcherServlet 它是SpringMVC中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。2、HanlerMapping 它是SpringMVC中完成url到Controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.
3、HandlerExecutionChain handler具体的执行器的一层包装类。
4、AnnotationMethodHandlerAdapter 处理器适配器,进行处理器中方法的调用。
5、ModelAndView ”模型和视图“,可以理解成MVC架构中的”M“和”V“。

二、源码分析。

在这里插入图片描述
上图流程总体来说可分为三大块:

Map的建立(并放入WebApplicationContext)

HttpRequest请求中Url的请求拦截处理(DispatchServlet处理)

反射调用Controller中对应的处理方法,并返回视图

本文将围绕这三块进行分析。

1. Map的建立
在容器初始化时会建立所有 url 和 Controller 的对应关系,保存到 Map<url,controller>中,那是如何保存的呢。

ApplicationObjectSupport #setApplicationContext方法

// 初始化ApplicationContext
@Override
public void initApplicationContext() throws ApplicationContextException {
    super.initApplicationContext();
    detectHandlers();
}

AbstractDetectingUrlHandlerMapping #detectHandlers()方法:

/**
 * 建立当前ApplicationContext 中的 所有Controller 和url 的对应关系
 */
protected void detectHandlers() throws BeansException {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
    }
    // 获取容器中的beanNames
    String[] beanNames = (this.detectHandlersInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
            getApplicationContext().getBeanNamesForType(Object.class));
    // 遍历 beanNames 并找到对应的 url
    // Take any bean name that we can determine URLs for.
    for (String beanName : beanNames) {
        // 获取bean上的url(class上的url + method 上的 url)
        String[] urls = determineUrlsForHandler(beanName);
        if (!ObjectUtils.isEmpty(urls)) {
            // URL paths found: Let's consider it a handler.
            // 保存url 和 beanName 的对应关系
            registerHandler(urls, beanName);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
            }
        }
    }
}

determineUrlsForHandler()方法:
该方法在不同的子类有不同的实现,我这里分析的是DefaultAnnotationHandlerMapping类的实现,该类主要负责处理@RequestMapping注解形式的声明。

/**
 * 获取@RequestMaping注解中的url
 */
@Override
protected String[] determineUrlsForHandler(String beanName) {
    ApplicationContext context = getApplicationContext();
    Class<?> handlerType = context.getType(beanName);
    // 获取beanName 上的requestMapping
    RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
    if (mapping != null) {
        // 类上面有@RequestMapping 注解
        this.cachedMappings.put(handlerType, mapping);
        Set<String> urls = new LinkedHashSet<String>();
        // mapping.value()就是获取@RequestMapping注解的value值
        String[] typeLevelPatterns = mapping.value();
        if (typeLevelPatterns.length > 0) {
            // 获取Controller 方法上的@RequestMapping
            String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType);
            for (String typeLevelPattern : typeLevelPatterns) {
                if (!typeLevelPattern.startsWith("/")) {
                    typeLevelPattern = "/" + typeLevelPattern;
                }
                for (String methodLevelPattern : methodLevelPatterns) {
                    // controller的映射url+方法映射的url
                    String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
                    // 保存到set集合中
                    addUrlsForPath(urls, combinedPattern);
                }
                addUrlsForPath(urls, typeLevelPattern);
            }
            // 以数组形式返回controller上的所有url
            return StringUtils.toStringArray(urls);
        }
        else {
            // controller上的@RequestMapping映射url为空串,直接找方法的映射url
            return determineUrlsForHandlerMethods(handlerType);
        }
    }
    // controller上没@RequestMapping注解
    else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
        // 获取controller中方法上的映射url
        return determineUrlsForHandlerMethods(handlerType);
    }
    else {
        return null;
    }
}

到这里,Controller和Url的映射就装配完成,下来就分析请求的处理过程。

2. url的请求处理
我们在xml中配置了DispatcherServlet为调度器,所以我们就来看它的代码,可以
从名字上看出它是个Servlet,那么它的核心方法就是doService()
DispatcherServlet #doService():

/**
 * 将DispatcherServlet特定的请求属性和委托 公开给{@link #doDispatch}以进行实际调度。
 */
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (logger.isDebugEnabled()) {
        String requestUri = new UrlPathHelper().getRequestUri(request);
        logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
                " request for [" + requestUri + "]");
    }

    //在包含request的情况下保留请求属性的快照,
    //能够在include之后恢复原始属性。
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        logger.debug("Taking snapshot of request attributes before include");
        attributesSnapshot = new HashMap<String, Object>();
        Enumeration attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // 使得request对象能供 handler处理和view处理 使用
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    try {
        doDispatch(request, response);
    }
    finally {
        // 如果不为空,则还原原始属性快照。
        if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
        }
    }
}

可以看到,它将请求拿到后,主要是给request设置了一些对象,以便于后续工作的处理(Handler处理和view处理)。比如WebApplicationContext,它里面就包含了我们在第一步完成的controller与url映射的信息。
DispatchServlet # doDispatch()

/**
 * 控制请求转发
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    int interceptorIndex = -1;

    try {

        ModelAndView mv;
        boolean errorView = false;

        try {
            // 1. 检查是否是上传文件
            processedRequest = checkMultipart(request);

            // Determine handler for the current request.
            // 2. 获取handler处理器,返回的mappedHandler封装了handlers和interceptors
            mappedHandler = getHandler(processedRequest, false);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                // 返回404
                noHandlerFound(processedRequest, response);
                return;
            }

            // Apply preHandle methods of registered interceptors.
            // 获取HandlerInterceptor的预处理方法
            HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
            if (interceptors != null) {
                for (int i = 0; i < interceptors.length; i++) {
                    HandlerInterceptor interceptor = interceptors[i];
                    if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                        triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                        return;
                    }
                    interceptorIndex = i;
                }
            }

            // Actually invoke the handler.
            // 3. 获取handler适配器 Adapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 4. 实际的处理器处理并返回 ModelAndView 对象
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // Do we need view name translation?
            if (mv != null && !mv.hasView()) {
                mv.setViewName(getDefaultViewName(request));
            }

            // HandlerInterceptor 后处理
            if (interceptors != null) {
                for (int i = interceptors.length - 1; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    // 结束视图对象处理
                    interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
                }
            }
        }
        catch (ModelAndViewDefiningException ex) {
            logger.debug("ModelAndViewDefiningException encountered", ex);
            mv = ex.getModelAndView();
        }
        catch (Exception ex) {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(processedRequest, response, handler, ex);
            errorView = (mv != null);
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            render(mv, processedRequest, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        // Trigger after-completion for successful outcome.
        // 请求成功响应之后的方法
        triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
    }

    catch (Exception ex) {
        // Trigger after-completion for thrown exception.
        triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
        throw ex;
    }
    catch (Error err) {
        ServletException ex = new NestedServletException("Handler processing failed", err);
        // Trigger after-completion for thrown exception.
        triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
        throw ex;
    }

    finally {
        // Clean up any resources used by a multipart request.
        if (processedRequest != request) {
            cleanupMultipart(processedRequest);
        }
    }
}

该方法主要是以下几个功能:

  • 通过request对象获取到HandlerExecutionChain,HandlerExecutionChain对象里面包含了拦截器interceptor和处理器handler。如果获取到的对象是空,则交给noHandlerFound返回404页面。

  • 拦截器预处理,如果执行成功则进行3

  • 获取handler适配器 Adapter

  • 实际的处理器处理并返回 ModelAndView 对象

3. 反射调用处理请求的方法,返回结果视图
在上面的源码中,实际的处理器处理并返回 ModelAndView 对象调用的是mv = ha.handle(processedRequest, response, mappedHandler.getHandler());这个方法。该方法由AnnotationMethodHandlerAdapter #handle() #invokeHandlerMethod()方法实现。

AnnotationMethodHandlerAdapter #handle() #invokeHandlerMethod()

/**
 * 获取处理请求的方法,执行并返回结果视图
 */
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    // 1.获取方法解析器
    ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
    // 2.解析request中的url,获取处理request的方法
    Method handlerMethod = methodResolver.resolveHandlerMethod(request);
    // 3. 方法调用器
    ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    ExtendedModelMap implicitModel = new BindingAwareModelMap();
    // 4.执行方法(获取方法的参数)
    Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
    // 5. 封装成mv视图
    ModelAndView mav =
            methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
    methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
    return mav;
}

三、总结。

我们队Spring mvc的整体流程进行概括。其实理解这些才是最重要的。

1、用户发送请求至前端控制器DispatcherServlet

2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

5、HandlerAdapter执行处理器(handler,也叫后端控制器)。

6、Controller执行完成返回ModelAndView

7、HandlerAdapter将handler执行结果ModelAndView返回给DispatcherServlet

8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器

9、ViewReslover解析后返回具体View对象
10、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值