博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring源码学习(8)——SpringMVC
阅读量:6654 次
发布时间:2019-06-25

本文共 29978 字,大约阅读时间需要 99 分钟。

spring框架提供了构建Web应用程序的全功能MVC模块。通过实现servlet接口的DispatcherServlet来封装其核心功能实现,通过将请求分派给处理程序,同时带有可配置的处理程序映射、视图解析、本地语言、主题解析以及上传下载文件支持。

SpringMVC的配置文件

1)配置web.xml

一个Web中可以没有web.xml文件,它主要用来初始化配置信息:例如welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。但SpringMVC的实现原理是通过Servlet拦截所有URL来达到控制的目的,所以web.xml的配置是必须的。

contextConfigLocation
/WEB-INF/applicationContext.xml
org.springframework.web.context.ContextLoaderListener
dispatcher
org.springframework.web.servlet.DispatcherServlet
1
dispatcher
/
index.jsp

这里面有两个关键的配置

  1、contextConfigLocation:Spring容器的所在位置。这个参数是使Web与Spring的配置文件相结合的关键配置

  2、DispatcherServlet:包含了SpringMvc的请求逻辑,Spring使用DispatcherServlet类拦截Web请求并进行相应的逻辑处理。

2)创建Spring配置文件applicationContext.xml

这里的配置就是用来存放应用所需的bean配置信息,和普通的ApplicationContext并无不同。

3)servlet的配置文件

默认的文件名就是servlet名+'_servlet.xml'

在Web启动时,服务器会加载对应于Servlet的配置文件,通常我们将Web部分的配置存放于此配置文件中。

 

容器之间的关系(个人理解)

这三个配置文件实际上对应的都是容器。首先web.xml对应的容器应该是tomcat加载web应用时首先加载的一个web容器。contextConfigLocation对应的是Spring的webApplicationContext,这是一个IOC容器,主要用来控制反转消除依赖用的。servlet对应的是servlet的容器

首先web容器中会通过listener持有IoC容器,这样web容器可以通过持有的IoC容器获取其中的bean。

其次web容器可以存放多个servlet,并不唯一,tomcat初始化servlet容器时,会将IoC容器作为此servlet容器的父容器,子容器将拥有访问父容器对象的权限,而父容器不可以访问子容器的权限。同时将其存放在web容器中。

 

1、ContextLoaderListener

我们首先先从web.xml开始。在编程方式的时候我们可以直接将配置信息作为参数传入Spring容器中,但在Web下,我们需要通过context-param的方式注册并使用ContextLoaderListener进行监听读取配置

ContextLoaderListener的作用就是启动Web容器的时候,自动装配applicationContext的配置信息。因为它实现了ServletContextListener这个接口,在启动容器的时候会默认执行它实现的方法,通过这个接口,我们可以在应用处理请求之前向servletContext中(也就是web容器)添加任意对象,这个对象在servletContext启动时被初始化,在整个servletContext运行期间都是可见的。

 

servletContext在启动之后会调用ServletContextListener的contextInitialized方法

public void contextInitialized(ServletContextEvent event) {        this.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!");        } else {            Log logger = LogFactory.getLog(ContextLoader.class);            servletContext.log("Initializing Spring root WebApplicationContext");            if(logger.isInfoEnabled()) {                logger.info("Root WebApplicationContext: initialization started");            }            long startTime = System.currentTimeMillis();            try {                if(this.context == null) {                    this.context = this.createWebApplicationContext(servletContext);                }                if(this.context instanceof ConfigurableWebApplicationContext) {                    ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context;                    if(!err.isActive()) {                        if(err.getParent() == null) {                            ApplicationContext elapsedTime = this.loadParentContext(servletContext);                            err.setParent(elapsedTime);                        }                        this.configureAndRefreshWebApplicationContext(err, servletContext);                    }                }                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);                ClassLoader err1 = Thread.currentThread().getContextClassLoader();                if(err1 == ContextLoader.class.getClassLoader()) {                    currentContext = this.context;                } else if(err1 != null) {                    currentContextPerThread.put(err1, this.context);                }                if(logger.isDebugEnabled()) {                    logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");                }                if(logger.isInfoEnabled()) {                    long elapsedTime1 = System.currentTimeMillis() - startTime;                    logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime1 + " ms");                }                return this.context;            } catch (RuntimeException var8) {                logger.error("Context initialization failed", var8);                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);                throw var8;            } catch (Error var9) {                logger.error("Context initialization failed", var9);                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);                throw var9;            }        }    }

这个方法做了几件事情

  1、WebApplicationContext存在性验证

如果servletContext中已经包含了此WebApplicationContext的话,就会抛出异常

  2、创建WebApplicationContext实例,通过createWebApplicationContext方法

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {        Class contextClass = this.determineContextClass(sc);        if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");        } else {            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);        }    }
protected Class
determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException var5) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5); } } }

首先先确定WebApplicationContext的类型,在determineContextClass方法中可以看到,如果配置中有contextClass时,则使用此类型,若没有,就是用默认的类型,该类型在defaultStrategies下可以找到

我们可以在ClassLoader.properties文件中看到默认类型是XmlWebApplicationContext

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

 

确定了context的类型后,就会使用反射实例化容器,之后再调用configureAndRefreshWebApplicationContext方法读取容器配置

  3、将实例记录在servletContext中

存放在servletContext的property中,对应的键为 WebApplicationContext.class.getName() + ".ROOT"

  4、映射当前的类加载器与创建的实例到全局变量currentContextPerThread中

 

 

2、DispatcherServlet

 servlet在初始化阶段首先会调用其init方法,我们可以再DispatcherServlet的父类HttpServletBean找到方法的实现

public final void init() throws ServletException {        if(this.logger.isDebugEnabled()) {            this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'");        }        HttpServletBean.ServletConfigPropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);        if(!pvs.isEmpty()) {            try {                BeanWrapper ex = PropertyAccessorFactory.forBeanPropertyAccess(this);                ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());                ex.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));                this.initBeanWrapper(ex);                ex.setPropertyValues(pvs, true);            } catch (BeansException var4) {                if(this.logger.isErrorEnabled()) {                    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");        }    }

  1、首先是对配置中的初始化参数进行封装,也就是servlet中配置的<init-param>,将其封装至propertyValue中。

  2、将当前的servlet实例转换成beanWrapper实例,PropertyAccessorFactory.forBeanPropertyAccess方法是spring中提供的工具方法,主要用于将指定实例转化为Spring中可以处理的BeanWrapper类型的实例

  3、注册Resource的属性编辑器,即在创建Bean的过程中一旦遇到了Resourcel类型的注入就使用ResourceEditor去解析。

  4、将之前封装的propertyValues放到生成的BeanWrapper中。

  5、接下来就是servletBean的初始化initServletBean()

 

初始化ServletBean的逻辑在FrameworkServlet中

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");        }    }

  1、WebApplicationContext的初始化

protected WebApplicationContext initWebApplicationContext() {        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());        WebApplicationContext wac = null;        if(this.webApplicationContext != null) {            wac = this.webApplicationContext;            if(wac instanceof ConfigurableWebApplicationContext) {                ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;                if(!attrName.isActive()) {                    if(attrName.getParent() == null) {                        attrName.setParent(rootContext);                    }                    this.configureAndRefreshWebApplicationContext(attrName);                }            }        }        if(wac == null) {            wac = this.findWebApplicationContext();        }        if(wac == null) {            wac = this.createWebApplicationContext(rootContext);        }        if(!this.refreshEventReceived) {            this.onRefresh(wac);        }        if(this.publishContext) {            String attrName1 = this.getServletContextAttributeName();            this.getServletContext().setAttribute(attrName1, wac);            if(this.logger.isDebugEnabled()) {                this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");            }        }        return wac;    }

首先当前的servlet会从servletContext中的缓存中取到WebApplicationContext,就是我们先前通过监听器添加的WebApplicationContext,对应的Key为WebApplicationContext.class.getName() + ".ROOT"

 取到的容器是dispacher-servlet的容器的父容器。

接下来就是寻找及创建当前servlet的WebApplicationContext了

  1、通过构造函数的的注入进行初始化

如果当前的servlet已经有了WebApplicationContext的话,说明这是通过构造函数传入的容器,那么接下来就会将之前找到的rootApplicationContext设置为其父容器,并执行configureAndRefreshWebApplicationContext方法

  2、通过contextAttribute进行初始化

如果在web.xml中的servlet设置了contextAttribute这个参数,那么这个容器在servletContext中存放的键名就是这个参数的值。因此接下来就会获取这个容器

protected WebApplicationContext findWebApplicationContext() {        String attrName = this.getContextAttribute();        if(attrName == null) {            return null;        } else {            WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName);            if(wac == null) {                throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");            } else {                return wac;            }        }    }

  3、如果上述两步还是没有找到对应的容器,那么就要重新实例化一个WebApplicationContext了

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {        Class contextClass = this.getContextClass();        if(this.logger.isDebugEnabled()) {            this.logger.debug("Servlet with name \'" + this.getServletName() + "\' will try to create custom WebApplicationContext context of class \'" + contextClass.getName() + "\', using parent context [" + parent + "]");        }        if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {            throw new ApplicationContextException("Fatal initialization error in servlet with name \'" + this.getServletName() + "\': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");        } else {            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);            wac.setEnvironment(this.getEnvironment());            wac.setParent(parent);            wac.setConfigLocation(this.getContextConfigLocation());            this.configureAndRefreshWebApplicationContext(wac);            return wac;        }    }

实例化的逻辑和之前的很相似,这里就不再赘述了。从这边我们可以看到最后也执行了configureAndRefreshWebApplicationContext方法。

 

configureAndRefreshWebApplicationContext

这个方法用来会已经实例化的WebApplicationContext进行配置及刷新

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {        if(ObjectUtils.identityToString(wac).equals(wac.getId())) {            if(this.contextId != null) {                wac.setId(this.contextId);            } else {                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());            }        }        wac.setServletContext(this.getServletContext());        wac.setServletConfig(this.getServletConfig());        wac.setNamespace(this.getNamespace());        wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null)));        ConfigurableEnvironment env = wac.getEnvironment();        if(env instanceof ConfigurableWebEnvironment) {            ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());        }        this.postProcessWebApplicationContext(wac);        this.applyInitializers(wac);        wac.refresh();    }

方法里主要对设置了一些属性,例如servletContext,servletConfig等,然后就是一些后处理器的执行:postProcessWebApplicationContext是一个空方法,为了支持拓展,applyInitializers方法则是找到在web.xml配置的初始化参数globalInitializerClasses,并执行其中ApplicationContextInitializer的initialize方法。

接下来就是refresh()方法了

这是一个模板方法,在之前将ApplicationContext容器的时候也说到过,不过之前只是对classPathXmlApplicationContext进行了分析,webApplicationContext的实现逻辑其实也类似

 

接下来到了刷新onRefresh,这也是一个模板方法

protected void onRefresh(ApplicationContext context) {        this.initStrategies(context);    }
protected void initStrategies(ApplicationContext context) {        this.initMultipartResolver(context);        this.initLocaleResolver(context);        this.initThemeResolver(context);        this.initHandlerMappings(context);        this.initHandlerAdapters(context);        this.initHandlerExceptionResolvers(context);        this.initRequestToViewNameTranslator(context);        this.initViewResolvers(context);        this.initFlashMapManager(context);    }

  1、初始化MultipartResolver

MultipartResolver主要用来处理文件上传的,默认情况下Spring是没有multipart处理的。如果要用的话,需要在容器中添加multipart解析器,这样每个请求都会被检查是否包含multipart,并使用容器中定义的resulver来解析它,常用配置如下:

解析器就是在initMultipartResolver方法中,被设置到此servlet中。

  2、初始化LocalResolver

支持国际化配置

  3、初始化ThemeResolver

支持主题功能

  4、初始化HandlerMappings

 当客户端发起Request时,DispatcherServlet会将Request提交给HandlerMapping,然后根据配置回传给相应的controller。

默认情况下,spring会加载所有实现handlerMapping接口的Bean,如果只期望Spring加载指定的HandlerMapping,那么可以修改Web.xml中DispatcherServlet的初始化参数,将detectAllHandlerMappings设置为false.

这样Spring会查找名为handlerMapping的bean,若没有找到对应bean,spring会在DispatcherServlet.property中查找org.Springframework.web.servlet.HandlerMapping的内容来加载HandlerMapping

  5、初始化HandlerAdapters

支持适配器,将Http请求对象和响应对象传递给Http请求处理器。

  6、初始化HandlerExceptionResolvers

提供异常处理

  7、初始化RequestToViewNameTranslator

  8、初始化ViewResolver

  9、初始化FlashMapManager

提供请求储存属性

 

DispatcherServlet的逻辑处理

在HttpServlet类中分别提供了相应的服务方法,例如doDelete() doGet()、doOptions()、doPost()、doPut()、doTrace(),根据不同的请求方式,servlet会引导至对应的方法中去。对于DispatcherServlet来说,对于不同的请求方式,都是统一交给processRequest()这一个方法来处理的。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        long startTime = System.currentTimeMillis();        Object failureCause = null;        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();        LocaleContext localeContext = this.buildLocaleContext(request);        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));        this.initContextHolders(request, localeContext, requestAttributes);        try {            this.doService(request, response);        } catch (ServletException var17) {            failureCause = var17;            throw var17;        } catch (IOException var18) {            failureCause = var18;            throw var18;        } catch (Throwable var19) {            failureCause = var19;            throw new NestedServletException("Request processing failed", var19);        } finally {            this.resetContextHolders(request, previousLocaleContext, previousAttributes);            if(requestAttributes != null) {                requestAttributes.requestCompleted();            }            if(this.logger.isDebugEnabled()) {                if(failureCause != null) {                    this.logger.debug("Could not complete request", (Throwable)failureCause);                } else if(asyncManager.isConcurrentHandlingStarted()) {                    this.logger.debug("Leaving response open for concurrent processing");                } else {                    this.logger.debug("Successfully completed request");                }            }            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);        }    }

从中我们可以看出具体的逻辑细节转移到了doService方法中了,但在这之前还是做了一些准备和处理工作的、

  1、首先提取了当前线程的LocaleContext以及RequestAttribute属性

  2、根据当前线程创建对应的localeContext和RequestAttribute,并绑定到当前线程

  3、doService做具体逻辑

  4、请求处理结束后恢复线程到原始状态

  5、请求结束后无论成功与否发布事件通知

 

接下来看doService的逻辑

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {        if(this.logger.isDebugEnabled()) {            String attributesSnapshot = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";            this.logger.debug("DispatcherServlet with name \'" + this.getServletName() + "\'" + attributesSnapshot + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");        }        HashMap attributesSnapshot1 = null;        if(WebUtils.isIncludeRequest(request)) {            attributesSnapshot1 = new HashMap();            Enumeration inputFlashMap = request.getAttributeNames();            label108:            while(true) {                String attrName;                do {                    if(!inputFlashMap.hasMoreElements()) {                        break label108;                    }                    attrName = (String)inputFlashMap.nextElement();                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));                attributesSnapshot1.put(attrName, request.getAttribute(attrName));            }        }        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());        FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response);        if(inputFlashMap1 != null) {            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1));        }        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);        try {            this.doDispatch(request, response);        } finally {            if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot1 != null) {                this.restoreAttributesAfterInclude(request, attributesSnapshot1);            }        }    }

实际上这个方法还是在做一些准备工作,包括生成request的属性快照、设置各种resolver等

完整的请求处理过程在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 {            try {                ModelAndView err = null;                Object dispatchException = null;                try {                    processedRequest = this.checkMultipart(request);                    multipartRequestParsed = processedRequest != request;                    mappedHandler = this.getHandler(processedRequest);                    if(mappedHandler == null || mappedHandler.getHandler() == null) {                        this.noHandlerFound(processedRequest, response);                        return;                    }                    HandlerAdapter err1 = this.getHandlerAdapter(mappedHandler.getHandler());                    String method = request.getMethod();                    boolean isGet = "GET".equals(method);                    if(isGet || "HEAD".equals(method)) {                        long lastModified = err1.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;                    }                    err = err1.handle(processedRequest, response, mappedHandler.getHandler());                    if(asyncManager.isConcurrentHandlingStarted()) {                        return;                    }                    this.applyDefaultViewName(processedRequest, err);                    mappedHandler.applyPostHandle(processedRequest, response, err);                } catch (Exception var20) {                    dispatchException = var20;                } catch (Throwable var21) {                    dispatchException = new NestedServletException("Handler dispatch failed", var21);                }                this.processDispatchResult(processedRequest, response, mappedHandler, err, (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);            }        }    }

  1、首先会先检查是否是文件上传

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {        if(this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {            if(WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {                this.logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, this typically results from an additional MultipartFilter in web.xml");            } else if(this.hasMultipartException(request)) {                this.logger.debug("Multipart resolution failed for current request before - skipping re-resolution for undisturbed error rendering");            } else {                try {                    return this.multipartResolver.resolveMultipart(request);                } catch (MultipartException var3) {                    if(request.getAttribute("javax.servlet.error.exception") == null) {                        throw var3;                    }                }                this.logger.debug("Multipart resolution failed for error dispatch", var3);            }        }        return request;    }

如果配置了MultipartResolver,并且当前request是multipartContent类型的话,则转换MultipartHttpServletRequest,并且调用MultipartResolver的resolverMultipart方法对文件做解析。

  2、根据request信息寻找对应的Handler

spring默认的handler是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping这两个

如果在配置中配置了<mvc:annotation-driven />的话,会使用RequestMappingHandlerMapping和BeanNameUrlHandlerMapping这两个Handler

我们看一下RequestMappingHandlerMapping的实现逻辑

首先spring会遍历所有的handler的getHandler()方法

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        Object handler = this.getHandlerInternal(request);        if(handler == null) {            handler = this.getDefaultHandler();        }        if(handler == null) {            return null;        } else {            if(handler instanceof String) {                String executionChain = (String)handler;                handler = this.getApplicationContext().getBean(executionChain);            }            HandlerExecutionChain executionChain1 = this.getHandlerExecutionChain(handler, request);            if(CorsUtils.isCorsRequest(request)) {                CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);                CorsConfiguration config = globalConfig != null?globalConfig.combine(handlerConfig):handlerConfig;                executionChain1 = this.getCorsHandlerExecutionChain(request, executionChain1, config);            }            return executionChain1;        }    }

根据request找到对应的handler

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);        if(this.logger.isDebugEnabled()) {            this.logger.debug("Looking up handler method for path " + lookupPath);        }        this.mappingRegistry.acquireReadLock();        HandlerMethod var4;        try {            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);            if(this.logger.isDebugEnabled()) {                if(handlerMethod != null) {                    this.logger.debug("Returning handler method [" + handlerMethod + "]");                } else {                    this.logger.debug("Did not find handler method for [" + lookupPath + "]");                }            }            var4 = handlerMethod != null?handlerMethod.createWithResolvedBean():null;        } finally {            this.mappingRegistry.releaseReadLock();        }        return var4;    }

这里首先获取了lookupPath对应的就是请求的uri

接下来根据缓存中解析得到的mapping得到对应的HandlerMethod

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {        HandlerExecutionChain chain = handler instanceof HandlerExecutionChain?(HandlerExecutionChain)handler:new HandlerExecutionChain(handler);        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);        Iterator var5 = this.adaptedInterceptors.iterator();        while(var5.hasNext()) {            HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();            if(interceptor instanceof MappedInterceptor) {                MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;                if(mappedInterceptor.matches(lookupPath, this.pathMatcher)) {                    chain.addInterceptor(mappedInterceptor.getInterceptor());                }            } else {                chain.addInterceptor(interceptor);            }        }        return chain;    }

然后将配置中的对应拦截器加入到责任链中。

 

  3、找到对应的HandlerAdapter

找到handlerAdaptor之后会调用其handle方法处理逻辑,对于之前提到的RequestMappingHandlerMapping对应的RequestMappingHandlerAdapter来说,就是执行之前的handlerMethod

 

转载于:https://www.cnblogs.com/wuzhe1991/p/8460760.html

你可能感兴趣的文章
Exchange企业实战技巧(5)配置OWA域名简写
查看>>
Nabou应用实例
查看>>
烂泥:ESXI开启SNMP服务
查看>>
《统一沟通-微软-实战》-6-部署-7-部署移动功能-2
查看>>
go语言笔记——调试还很弱,用gdb来做?可用panic和defer。格式化代码使用gofmt,貌似我的vim插件是自带...
查看>>
Linux 安装.src.rpm源码包的方法
查看>>
c#将对象序列化为字符串和将字符串反序列化为对象
查看>>
Android Loader详解四:回调及完整例子
查看>>
Oracle笔记 三、function 、select
查看>>
PHP5.5面向对象连接mysqli
查看>>
一步一步教你使用AgileEAS.NET基础类库进行应用开发-WinForm应用篇-在UI中应用DataUIMapper组件...
查看>>
Linux命令大全
查看>>
git 拉取和获取 pull 和 fetch 区别
查看>>
html5系列目录
查看>>
C# 视频监控系列(1):准备
查看>>
6.3. 获取当前用户
查看>>
软件架构中的层次依赖
查看>>
两个容易被忽略的mysql知识
查看>>
ORACLE SOA SUITE ORABPEL-12133 错误解决
查看>>
除了新闻识别,这家媒体还利用AI管理内容分发,2500万人已关注
查看>>