zoukankan      html  css  js  c++  java
  • 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、流程图

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

  • 相关阅读:
    Effective Java 第三版——26. 不要使用原始类型
    Effective Java 第三版——25. 将源文件限制为单个顶级类
    Effective Java 第三版——24. 优先考虑静态成员类
    Effective Java 第三版——23. 优先使用类层次而不是标签类
    Effective Java 第三版——22. 接口仅用来定义类型
    Effective Java 第三版——21. 为后代设计接口
    Effective Java 第三版——20. 接口优于抽象类
    Effective Java 第三版——19. 如果使用继承则设计,并文档说明,否则不该使用
    Effective Java 第三版——18. 组合优于继承
    Effective Java 第三版——17. 最小化可变性
  • 原文地址:https://www.cnblogs.com/pjfmeng/p/14440324.html
Copyright © 2011-2022 走看看