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、流程图

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

  • 相关阅读:
    Schedular
    CronTrigger
    SimpleTrigger
    Quartz代码示例
    quartz
    gson and json
    json数据结构和gson的比较
    stackApp符号匹配
    Git命令
    org.apache.maven.archiver.mavenarchiver.getmanifest怎么解决
  • 原文地址:https://www.cnblogs.com/pjfmeng/p/14440324.html
Copyright © 2011-2022 走看看