springmvc源码解析

1、主要组件

springmvc包括以下组件,主要有以下作用    

         1、前端控制器(DispatcherServlet)  接收用户请求,发送响应

        2、处理器映射器(HandlerMapping)根据请求的url来查找handler   如:

                    SimpleUrlHandlerMapping基于手动配置 url control 映谢

        RequestMappingHandlerMapping基于@RequestMapping注解配置对应映谢

       3、处理器适配器(HandlerAdapter)适配handle然后执行

            SimpleControllerHandlerAdapter  对应实现controller接口的处理器

            HttpRequestHandlerAdapter 对应实现HttpRequestHandler 接口的处理器

            SimpleServletHandlerAdapter对应实现HttpServlet 接口的处理器

            RequestMappingHandlerAdapter 对应@requestMapping注解的处理器

      4、处理器(Handler)按照适配器的要求的规则去编写handler

               Controller 接口:

               HttpRequestHandler 接口:

               HttpServlet 接口:

              @RequestMapping方法注解

           可以看出 Handler 没有统一的接口,这时候就需要处理器适配器适配,然后通过适配器指定handle

      5、视图解析器(ViewResolver)

2、源码解析

2.1容器初始化

在没使用springboot的时候我们配置springmvc的时候,会在web.xml中配置如下内容

   <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:application-context.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

可以看到我们配置了ContextLoaderListener,他实现了ServletContextListener,在xml中配置了这个监听器,启动容器时,会自动执行实现的contextInitialized()方法,

在ServletContextListener中的核心逻辑便是初始化WebApplicationContext实例并存放至ServletContext中,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。

public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            throw new IllegalStateException(
                    "Cannot initialize context because there is already a root application context present - " +
                    "check whether you have multiple ContextLoader* definitions in your web.xml!");
        }

        servletContext.log("Initializing Spring root WebApplicationContext");
        Log logger = LogFactory.getLog(ContextLoader.class);
        if (logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            // Store context in local instance variable, to guarantee that
            // it is available on ServletContext shutdown.
            // 创建得到WebApplicationContext,也就是ioc容器
            // createWebApplicationContext最后返回值被强制转换为ConfigurableWebApplicationContext类型
            if (this.context == null) {
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
//强制转换为ConfigurableWebApplicationContext  ConfigurableWebApplicationContext cwac
= (ConfigurableWebApplicationContext) this.context;
// cwac尚未被激活,目前还没有进行配置文件加载
if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); }
//加载配置文件,也就是上面配置的applicationContext.xml文件 configureAndRefreshWebApplicationContext(cwac, servletContext); } }
//将ioc设置为自己的父容器,这样的话子容器可以访问父容器,但是父容器(ioc容器)不能访问子容器(DispatcherServlet) servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException | Error ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; }

然后开始初始化DispatcherServlet,DispatcherServlet实现了Servlet接口,

public interface Servlet {

    /**
     * 容器启动时被调用(当load-on-startup为负数或者不设置时,会在第一次被使用时才调用),只会调用一次
     * 它有一个参数ServletConfig,是容器传进来的,表示的是这个Servlet的一些配置,比如DispatcherServlet配置的<init-param>
     */
    public void init(ServletConfig config) throws ServletException;

    /**
     * 获取Servlet的配置
     */
    public ServletConfig getServletConfig();

    /**
     * 最重要的一个方法,是具体处理请求的地方
     */
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    /**
     * 获取Servlet的一些信息,比如作者、版本、版权等,需要子类实现
     */
    public String getServletInfo();

    /**
     * 用于Servlet销毁(主要指关闭容器)时释放一些资源,只会调用一次
     */
    public void destroy();
}

init方法开始初始化容器

public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
        logger.debug("Initializing servlet '" + getServletName() + "'");
    }

    // 获取Servlet的初始化参数,对Bean属性进行配置
    try {
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        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) {
        logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
        throw ex;
    }

    // 调用子类的initServletBean方法进行具体的初始化工作
    initServletBean();

    if (logger.isDebugEnabled()) {
        logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
}

// initServletBean这个初始化工作位于FrameworkServlet类中
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    // 这里初始化上下文
    try {
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                elapsedTime + " ms");
    }
}

protected WebApplicationContext initWebApplicationContext() {
    // 在这里获取父容器,那什么时候放进去的呢,也就是上面的ContextLoaderListener时放的
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
    //DispatcherServlet作为spring的bean,将SpringMVC容器注入,这里我们直接拿来作为SpringMVC容器
        onRefresh(wac);
    }

    // 把SpringMVC容器注册到ServletContext中去,根据context id去获取
    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                    "' as ServletContext attribute with name [" + attrName + "]");
        }
    }

    return wac;
}

protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
   //这里就到了初始化DispatcherServlet
   //实现文件上传功能
   initMultipartResolver(context);
   //初始化本地解析器
   initLocaleResolver(context);
   //初始化主题解析器
   initThemeResolver(context);
   //初始化处理器映射器,将请求映射到处理器
   initHandlerMappings(context);
   //初始化处理器适配器
   initHandlerAdapters(context);
   //初始化处理器异常解析器,如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
   initHandlerExceptionResolvers(context);
   //初始化请求到具体视图名称解析器
   initRequestToViewNameTranslator(context);
   //初始化视图解析器,通过ViewResolver解析逻辑视图名到具体视图实现
   initViewResolvers(context);
   //初始化flash映射管理
   initFlashMapManager(context);
}

2.2处理请求

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                //文件上传解析
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                //获取处理器映射器,这里返回的是一个拦截器链
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //获取处理器适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //调用拦截器的preHandle方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                处理器方法执行,包括参数处理和返回值处理
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                //调用拦截器的postHandle方法
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            //解析视图,处理异常
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //调用拦截器的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

3、拦截器

public interface HandlerInterceptor {

    /**
     *预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller 
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);    
* false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
*/
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

    /**
     * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。*/
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
     * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,
* 但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
*/ default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }

可以一些权限验证或者日志输出,请求耗时等等

当然filter也可以实现这些,

4、参数处理器

4.1HandlerMethodArgumentResolver  自定义参数处理器

默认处理器:RequestParamMethodArgumentResolver

public interface HandlerMethodArgumentResolver {

    /**
     * 所有方法中的参数都需要调用此方法判断此解析器是否支持此参数 
     */
    boolean supportsParameter(MethodParameter parameter);

    /**
     * 将方法参数解析为来自给定请求的参数值
     */
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

这时候自定义的处理器可能会不生效,因为HandlerMethodArgumentResolverComposite保存了HandlerMethodArgumentResolver的数组对象,一旦supportsParameter返回true就会使用这个参数处理器,所以可能需要手动改变HandlerMethodArgumentResolverComposite

里保存的Resolver数组的顺序,将自定义的放在第一位。

4.2、HandlerMethodReturnValueHandler 返回值处理器

public interface HandlerMethodReturnValueHandler {

    /**
     * 支持哪类类型的返回值将将使用该返回值处理器
     */
    boolean supportsReturnType(MethodParameter returnType);

    /**
     * 主要处理返回值的处理逻辑,并且将处理好的值返回给model
     */
    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

HandlerMethodReturnValueHandler 和上面一样,也是保存在HandlerMethodReturnValueHandlerComposite类的数组中,也需要注意下顺序

这种可以运用在比如系统同一封装返回json的格式类型

4.3 HandlerExceptionResolver  异常处理

public interface HandlerExceptionResolver {

    /**
     * 对请求返回的异常同一处理
     */
    @Nullable
    ModelAndView resolveException(
            HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}

在Springboot中可以直接用全局异常处理器(@ControllerAdvice结合@ExceptionHandler(Exception.class))

5、springmvc父子容器

上面有简短的介绍,这里总结下

spring总的上下文容器有父子之分,父容器和子容器。 ** 父容器对子容器可见,子容器对父容器不可见 ** 。

对于传统的spring mvc来说,spring mvc容器为子容器,也就是说ServletDispatcher对应的容器为子容器,而web.xml中通过ConextLoaderListener的contextConfigLocation属性配置的为父容器。

也就是@controller是有springmvc加载的子容器,@Service等等其他的注解是有父容器加载的,所以可能导致修饰@Controller的事务不可用,因为他们不由spring管理

不过现在springboot没有这种问题,不需要使用tomact容器,使用内嵌的tomact,是一个容器。

6、流程图

网上找了个流程图,画的还不错 如下:

原文地址:https://www.cnblogs.com/pjfmeng/p/14440324.html