spring 源码解读-MVC

spring 源码解读-MVC

一、MVC请求处理流程

  1. DispatcherServlet是SpringMVC中的前端控制器,负责接收request ,并将request转发给对应组件。
  2. HanderMapping是完成url到Controller的映射组件,DispatcherServlet接收request ,然后前端控制器请求处理器映射器。(HandlerMapping)去查找处理器(Handler),找到以后处理器映射器(HandlerMappering)向前端控制器返回执行链(HandlerExecutionChain)。
  3. 前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)。
  4. 处理器适配器去执行Handler,处理器执行完向前端控制器返回ModelAndView。
  5. 前端控制器请求视图解析器(ViewResolver)去进行视图解析。然后视图解析器向前端控制器返回View。
  6. 前端控制器对视图进行渲染并向用户响应结果。

二、MVC九大组件

  1. HanderMapping:处理器映射器,用来查找Hander,具体的表现形式可以是类也可以是方法,HanderMapping在请求到达后,它的作用就是找到请求相应的处理器Hander和Interceptors。
  2. HandlerAdapter:处理器适配器,SpringMVC中的Hander可以是任意形式,只要能处理请求就行,由于Servlet的方法结构都是如 doService(HttpServletRequest req,HttpServletResponse rsp) 这样的形式,让固定的Servlet处理方法调用Hander来进行处理,这一步就要HandlerAdapter来执行。
  3. HandlerExceptionResolvers:这个是来处理Hander过程中出现的异常情况,它的作用就是根据异常来设置ModelAndView页面,它只负责请求处理阶段的异常,而渲染阶段的异常则不属于它。
  4. ViewResolver:视图解析器,它的作用是将String类型的视图和Locale解析为View类型的视图,Controller返回的String类型的视图名称ViewName,最终会被解析成View(渲染页面)
  5. RequestToViewNameTranslator:作用就是从Request中获取ViewName,因为ViewResolver是根据ViewName查找View的,但是有的Hander处理完成之后,没有设置View也没有ViewName,这个时候就需要这个组件从Request中获取ViewName
  6. LocaleResolver:ViewResolver的resolveViewResolvers()方法,需要两个参数,第二个参数 Locale的产生,就是LocaleResolver的作用,LocaleResolver从request中解析出Locale,中国大陆这个值就是zh-CN表示一个区域,这个也是i18n国际化的基础。
  7. ThemeResolver:主题解析,主题就是样式css、图片以及它们形成的显示效果集合,一套主题对应一个porperties文件,里面存放和主题相关的所有资源。ThemeResolver从request中解析出主题名,找到对应的具体主题。
  8. MultipartResolver:用于处理上传请求,将普通的request包装成MultipartHttpServletRequest 来实现,HttpServletRequest 可以通过getFile() 直接获取文件,如果是多文件上传,还可以通过调用getFileMap()得到 Map(FileName,File) 这样的结构。
  9. FlashMapManager:FlashMap是用于重定向 Redirect时参数数据传递,因为Redirect没有参数传递的功能,只能通过FlashMap来传递,在Redirect之前将参数写入request。FlashMapManager就是用来管理FlashMap的。

三、源码分析

主要分三个部分:

  • 一、ApplicationContext初始化时,用Map保存所有的url和Controller类的对应关系;
  • 二、根据url找到对应的Controller,并从Controller中找到处理请求的方法;
  • 三、request的参数绑定到方法形参,执行方法处理,并返回结果视图。

1、初始化

DispatcherServlet 里的init()方法,然后发现其init()方法是在父类HttpServletBean中,源码如下:

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

        try {
            PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            this.initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        } catch (BeansException var4) {
            this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
            throw var4;
        }

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

    }

继续跟踪 this.initServletBean();进入:


    protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
        }

        long startTime = System.currentTimeMillis();

        try {
            this.webApplicationContext = this.initWebApplicationContext();
            this.initFrameworkServlet();
        } catch (ServletException var5) {
            this.logger.error("Context initialization failed", var5);
            throw var5;
        } catch (RuntimeException var6) {
            this.logger.error("Context initialization failed", var6);
            throw var6;
        }

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

    } 

这里主要逻辑就是初始化IOC容器,最终会调用 refresh()方法,前面IOC容器的初始化讲过,IOC容器初始化之后最终调用onRefresh()方法,最终是在DispatcherServlet中实现,源码如下:

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

    protected void initStrategies(ApplicationContext context) {
       //初始化多文件上传组件
        this.initMultipartResolver(context);
        //初始化本地语言环境
        this.initLocaleResolver(context);
        //初始化主题模板解析器
        this.initThemeResolver(context);
        //initHandlerMappings初始化处理器映射器
        this.initHandlerMappings(context);
        //初始化处理器适配器
        this.initHandlerAdapters(context);
        //初始化异常拦截器
        this.initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        this.initRequestToViewNameTranslator(context);
        //初始化视图处理器
        this.initViewResolvers(context);
        //初始化FlashMap管理器
        this.initFlashMapManager(context);
    }

到这一步初始化动作完成,下一步主要看initHandlerMappings,映射关系的建立,主要是HandlerMapping接口的子类AbstractDetectingUrlHandlerMapping类的initApplicationContext方法,源码如下:


    public void initApplicationContext() throws ApplicationContextException {
        super.initApplicationContext();
        this.detectHandlers();
    }

    protected void detectHandlers() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Looking for URL mappings in application context: " + this.getApplicationContext());
        }

        String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class) : this.getApplicationContext().getBeanNamesForType(Object.class);
        String[] var2 = beanNames;
        int var3 = beanNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String beanName = var2[var4];
            String[] urls = this.determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                this.registerHandler(urls, beanName);
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
            }
        }

    }

determineUrlsForHandler 方法作用就是获取每个Controller中url,不同的子类有不同的实现方式,这个就是典型的模板方法模式,BeanNameUrlHandlerMapping就是AbstractDetectingUrlHandlerMapping类的子类,处理注解形式的url映射,目前都是注解开发,所以BeanNameUrlHandlerMapping是如何查BeanName上映射的url?

  protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList();
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }

        String[] aliases = this.getApplicationContext().getAliases(beanName);
        String[] var4 = aliases;
        int var5 = aliases.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String alias = var4[var6];
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }

        return StringUtils.toStringArray(urls);
    }

这里就完成了HanderMapping映射关系。

2、运行

DispatcherServlet 的核心方法doService()方法,该方法中的核心逻辑是doDispatch(),源码如下:


    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;
            Object dispatchException = null;

            try {
                //检查是否文件上传请求
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                //获取当前的Control,Handler处理器,
                //并不是直接返回Handler,而且返回处理器链对象,封装了Handler和intercept
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }

               //获取处理request的处理器适配器
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }

                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                //实际的处理器处理请求,返回结果视图对象MV
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                //结果视图对象的处理
                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    //请求成功响应之后的方法
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }

    }

具体调用RequestMappingHandlerAdapter的handle()方法,核心逻辑handleInternal()方法

   protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        this.checkRequest(request);
        ModelAndView mav;
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized(mutex) {
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader("Cache-Control")) {
            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
                this.prepareResponse(response);
            }
        }

        return mav;
    }

整个处理过程最核心的逻辑就是拼接controller的url和方法的url与request的url进行匹配,找到匹配的方法。


    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        ModelAndView var15;
        try {
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
            Object result;
            if (asyncManager.hasConcurrentResult()) {
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Found concurrent result value [" + result + "]");
                }

                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if (asyncManager.isConcurrentHandlingStarted()) {
                result = null;
                return (ModelAndView)result;
            }

            var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
        } finally {
            webRequest.requestCompleted();
        }

        return var15;
    }

invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]); 最终要实现的目的就是:完成request中的参数和方法参数上的数据绑定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值