美文网首页
spring mvc源码解析

spring mvc源码解析

作者: binecy | 来源:发表于2018-01-18 14:59 被阅读72次

    源码分析基于spring 4.3.x
    文章主要记录看spring mvc源码时的一些关键点。不当之处,还望指出。

    HttpServlet接口结构

    HttpServlet.png

    HttpServletBean

    HttpServletBean是接口结构第一个Spring的类,它从web.xml或WebApplicationInitializer接收servlet的init-param值,注入到bean的属性。

    FrameworkServlet

    FrameworkServlet集成了Servlet功能与Spring web application context上下文,实现了ApplicationContextAware接口。 但它也能够自行创建web application context。

    HttpServletBean父类将init-params注入为bean属性。

    HttpServlet中doGet/doPost/doPut/doDelete等方法,统一调用processRequest方法处理(该方法最终会调用DispatcherServlet.doService)

    DispatcherServlet

    DispatcherServlet是springmvc的核心类,实现了doService方法,
    负责请求准备,Flash映射,请求分派(doDispatch),异常处理等工作。

    doDispatch抽取核心代码如下

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            ModelAndView mv = null;
            try {
                // 查找handler
                mappedHandler = getHandler(processedRequest);
            
                // 查找HandlerAdapter
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                // 调用HandlerInterceptor.preHandle
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
    
                // 调用handler处理
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                applyDefaultViewName(processedRequest, mv);
    
                // 调用HandlerInterceptor.preHandlepostHandle
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception ex) {
                ...
            }
    
            // 结果处理
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        finally {
            // 资源清理
            ...
        }
    }
    

    HandlerMapping

    HandlerMapping.png

    HandlerMapping负责定义requests和handler的映射关系。

    AbstractHandlerMapping.getHandler获取HandlerExecutionChain,HandlerExecutionChain是对handler的一个扩展,包含了handler(一般是HandlerMethod)和interceptors
    AbstractHandlerMapping.getHandlerInternal获取对应的处理方法HandlerMethod,通过lookupHandlerMethod方法查找。

    AbstractHandlerMethodMapping 实现了InitializingBean, afterPropertiesSet方法查找并解析所有RequestMapping注解的方法,并将结果存储在mappingRegistry属性中。

    HandlerAdapter

    HandlerAdapter.png

    HandlerAdapter负责使用handler处理requests,核心方法ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    spring中提供了多种HandlerAdapter, SimpleServletHandlerAdapter/SimpleControllerHandlerAdapter/RequestMappingHandlerAdapter(springmvc使用)。

    RequestMappingHandlerAdapter.invokeHandlerMethod方法具体实现了方法调用

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            // 获取方法
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
    
            return getModelAndView(mavContainer, modelFactory, webRequest);
    
    }       
    

    ServletInvocableHandlerMethod.invokeAndHandle负责参数解析,方法调用,结果处理。

    参数解析

    HandlerMethod.png

    HandlerMethod是对RequestMapping映射方法的抽象。

    InvocableHandlerMethod.getMethodArgumentValues

    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        // 获取方法参数
        MethodParameter[] parameters = getMethodParameters();
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = resolveProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            // argumentResolvers是否支持
            if (this.argumentResolvers.supportsParameter(parameter)) {
                    //  argumentResolvers解析
                    args[i] = this.argumentResolvers.resolveArgument(
                            parameter, mavContainer, request, this.dataBinderFactory);
                    continue;
                }
                
            }
            ...
        }
        return args;
    }
    

    参数解析通过HandlerMethodArgumentResolver完成。


    HandlerMethodArgumentResolver.png

    RequestParamMethodArgumentResolver负责处理@RequestParam注解的参数
    PathVariableMethodArgumentResolver负责处理@PathVariable注解的参数
    RequestResponseBodyMethodProcessor负责处理 @RequestBody 注解,
    从AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters中可以看到,它会根据HttpReqeust的contentType,查找对应的HttpMessageConverter进行处理。
    AbstractMessageConverterMethodArgumentResolver.messageConverters属性保存所有的HttpMessageConverter,包括json, xml的转化。

    结果处理

    方法调用结果有两个地方处理。

    1. ServletInvocableHandlerMethod.handleReturnValue
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 查找HandlerMethodReturnValueHandler
        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        // 结果处理
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
    

    我们一般使用@ResponseBody注解方法,声明返回json结果。@ResponseBody注解也是由RequestResponseBodyMethodProcessor进行处理的。
    AbstractMessageConverterMethodProcessor.writeWithMessageConverters转化结果并输出到客户端。

    1. DispatcherServlet.processDispatchResult负责渲染ModelAndView

    HandlerInterceptor

    HandlerInterceptor也是我们常用的功能。HandlerInterceptor类似于Servlet 规范中的过滤器Filter,可以对每个request进行预处理和后处理。通过HandlerInterceptor,我们可以实现身份认证,日志等功能。

    前面说过了,HandlerExecutionChain中包含了HandlerInterceptor,DispatcherServlet.doDispatch在handler处理前后会调用mappedHandler.applyPreHandlemappedHandler.applyPostHandle,而mappedHandler.triggerAfterCompletion在ModelAndView渲染后调用。

    异常处理

    springmvc提供了多种处理异常的方法,如Controller中定义

    @Controller
    public class SimpleController {
    
        // @RequestMapping methods omitted ...
    
        @ExceptionHandler(IOException.class)
        public ResponseEntity<String> handleIOException(IOException ex) {
            // prepare responseEntity
            return responseEntity;
        }
    
    }
    

    DispatcherServlet.processHandlerException负责处理异常

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {
    
        // Check registered HandlerExceptionResolvers...
        ModelAndView exMv = null;
        for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
            exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
        ...
        throw ex;
    }
    

    springmvc提供了三种HandlerExceptionResolver实现,ExceptionHandlerExceptionResolver/DefaultHandlerExceptionResolver/ResponseStatusExceptionResolver,
    上面@ExceptionHandler注解方法就是由ExceptionHandlerExceptionResolver进行处理。

    我们也可以自定义HandlerExceptionResolver的实现,自行进行异常处理。

    可参考 sprimgmvc-1.6. Exception Handling

    Spring ApplicationContext层次

    springmvc中有两处配置会创建Spring ApplicationContext。

    ContextLoaderListener

    ContextLoaderListener实现了接口,当web容器启动时,会调用他的contextInitialized方法,web容器关闭时会调用contextDestroyed方法。
    ContextLoaderListener会创建ApplicationContext。

    先看看web.xml配置

        <listener>
            <listener-class>
                org.springframework.web.context.ContextLoaderListener
            </listener-class>
        </listener>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-core-config.xml</param-value>
        </context-param>
    

    ContextLoaderListener.contextInitialized主要调用ContextLoader.initWebApplicationContext

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        ...
        if (this.context == null) {
            // 默认创建XmlWebApplicationContext
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            if (!cwac.isActive()) {
                ...
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }
        // 将ApplicationContext放到 ServletContext
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    
       
        return this.context;
    }
    

    注意:servletContext.setAttribute 这里将这个spring ApplicationContext放到了ServletContext中(后面会使用到它)。

    configureAndRefreshWebApplicationContext会找到ServletContext中contextConfigLocation参数(这里是spring-core-config.xml)作为ApplicationContext配置路径,并refresh

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        
        ...
        wac.setServletContext(sc);
        String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }
    
        wac.refresh();
    }
    

    FrameworkServlet

    FrameworkServlet.initServletBean方法也会创建一个spring context。
    web.xml的配置

        <servlet>
            <servlet-name>hello-dispatcher</servlet-name>
            <servlet-class>
                org.springframework.web.servlet.DispatcherServlet
            </servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/spring-mvc-config.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    

    可以看看他的initWebApplicationContext

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
    
        if (wac == null) {
            // 从ServletContext中查找WebApplicationContext
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // 创建WebApplicationContext
            wac = createWebApplicationContext(rootContext);
        }
    
        if (!this.refreshEventReceived) {
            onRefresh(wac);
        }
    
        return wac;
    }
    

    这里的rootContext,会找到ContextLoaderListener中创建的WebApplicationContext

    看看createWebApplicationContext

    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        // 默认XmlWebApplicationContext
        Class<?> contextClass = getContextClass();
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    
        wac.setEnvironment(getEnvironment());
        // 设置parent
        wac.setParent(parent);
        // 设置配置路径
        wac.setConfigLocation(getContextConfigLocation());
        // refresh
        configureAndRefreshWebApplicationContext(wac);
    
        return wac;
    }
    

    这里同样将contextConfigLocation参数(这里是spring-mvc-config.xml)作为WebApplicationContext的配置路径。

    要注意的是,这里将ContextLoaderListener中创建的WebApplicationContext作为parent了。

    ApplicationContext查找bean时,会先从parent context中查找。所以,FrameworkServlet Application可以找到ContextLoaderListener Application中定义的bean,但ContextLoaderListener Application找不到FrameworkServlet Application中定义的bean。这点值得注意,以免出现bean注入失败的异常。

    相关文章

      网友评论

          本文标题:spring mvc源码解析

          本文链接:https://www.haomeiwen.com/subject/yxvyoxtx.html