您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Spring MVC 核心调用流程

时间:2020-10-10 11:41:23  来源:  作者:

概述 :

 

DispatcherServlet作为Spring MVC的核心控制器,初始化组件,处理客户端发送的请求,并返回 ModelAndView,进行视图渲染。主要是实现了父类 FrameworkServlet的抽象方法 doService()。

 

DispatcherServlet 类图

Spring MVC 核心调用流程

 

DispatcherServlet的 initStrategies方法如何被调用的

入口类:AbstractApplicationContext # refresh

├─ refresh

│├─ finishRefresh

││└─ publishEvent

│││├─ publishEvent

│││└─ multicastEvent

││││└─ invokeListener

│││││└─ doInvokeListener

││││││└─ onApplicationEvent

│││││││└─ onRefresh

││││││││└─ onRefresh

│││││││││└─ initStrategies

进入 onRefresh方法

所在类:org.springframework.web.servlet. DispatcherServlet

    protected void onRefresh(ApplicationContext context) {        initStrategies(context);    }    protected void initStrategies(ApplicationContext context) {        // 初始化文件上传处理        initMultipartResolver(context);        // 初始化本地化 Resolver        initLocaleResolver(context);        // 初始化主题 Resolver        initThemeResolver(context);        // 初始化 URL映射关系        initHandlerMappings(context);        // 初始化Handler接口适配器        initHandlerAdapters(context);        // 初始化异常处理的 handler        initHandlerExceptionResolvers(context);        // 初始化请求路径转换        initRequestToViewNameTranslator(context);        // 初始化视图解析        initViewResolvers(context);        // 初始化 flashmap管理        initFlashMapManager(context);    }

::: warning 知识点 initStrategies方法中的所有初始化组件中之所以可以拿到值,主要是通过 @EnableWebMvc注解,调用到 WebMvcConfigurationSupport类中的各个 @Bean注解的方法,完成的实例化过程。:::

请求调用流程

当父子容器都启动完成后,开始进行请求的响应处理,

  • 请求 http://localhost:9090/user/queryUser地址

进入 service方法

所在类:JAVAx.servlet.http. HttpServlet

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        String method = req.getMethod();        long lastModified;        if (method.equals("GET")) {            lastModified = this.getLastModified(req);            if (lastModified == -1L) {                this.doGet(req, resp);            }           // ...... 省略    }

进入 doGet方法

所在类:org.springframework.web.servlet. FrameworkServlet

@Override    protected final void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        processRequest(request, response);    }

进入 processRequest方法

所在类:org.springframework.web.servlet. FrameworkServlet

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        long startTime = System.currentTimeMillis();        Throwable failureCause = null;        doService(request, response);}

进入 doService方法

所在类:org.springframework.web.servlet. DispatcherServlet

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {        logRequest(request);        try {            // 调用核心流程            doDispatch(request, response);        }        finally {            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {                // Restore the original attribute snapshot, in case of an include.                if (attributesSnapshot != null) {                    restoreAttributesAfterInclude(request, attributesSnapshot);                }            }        }}

请求调用 核心入口

请求最终进入 doDispatch方法

所在类:org.springframework.web.servlet. DispatcherServlet

    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 {                // 文件上传解析,如果请求类型是multipart将通过                // MultipartResolver进行文件上传解析                processedRequest = checkMultipart(request);                multipartRequestParsed = (processedRequest != request);                // 对当前请求匹配一个合适的 handler,重要方法                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;                    }                }                if (!mappedHandler.applyPreHandle(processedRequest, response)) {                    return;                }                // Actually invoke the handler.                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());                if (asyncManager.isConcurrentHandlingStarted()) {                    return;                }                applyDefaultViewName(processedRequest, mv);                mappedHandler.applyPostHandle(processedRequest, response, mv);            }            catch (Exception ex) {                dispatchException = ex;            }            catch (Throwable err) {                dispatchException = new NestedServletException("Handler dispatch failed", err);            }            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);        }        catch (Exception ex) {            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 {                // 如果是multipart的请求,清空上传的multipart资源                if (multipartRequestParsed) {                    cleanupMultipart(processedRequest);                }            }        }    }

::: warning 知识点总结, getHandler方法的主要作用体现在以下几点:

  • 首先,从当前Request中拿到请求的 URL
  • 然后,从映射关系中拿到 HandlerMethod对象
  • 接着,把 HandlerMethod对象封装到 HandlerExecutionChain执行链中
  • 最后,在 HandlerExecutionChain执行链的创建过程中会拿到整个容器中所有的拦截器(实现 HandlerInterceptor接口的拦截器),和当前请求的 URL进行匹配,如果匹配成功的话,就会把拦截器放到 HandlerExecutionChain的数组中。:::

进入 getHandler方法

所在类:org.springframework.web.servlet. DispatcherServlet

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        // handlerMappering 实例容器不为空        if (this.handlerMappings != null) {            for (HandlerMapping mapping : this.handlerMappings) {                // 获取 HandlerMethod 和过滤器链的包装类                HandlerExecutionChain handler = mapping.getHandler(request);                if (handler != null) {                    return handler;                }            }        }        return null;    }

进入 getHandler方法

所在类:org.springframework.web.servlet.handler. AbstractHandlerMapping

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        // 根据请求的 URL 拿到对应的 HandlerMethod 对象        Object handler = getHandlerInternal(request);        if (handler == null) {            handler = getDefaultHandler();        }        if (handler == null) {            return null;        }        // Bean name or resolved handler?        if (handler instanceof String) {            String handlerName = (String) handler;            handler = obtainApplicationContext().getBean(handlerName);        }        // 获取 HandlerMethod 和过滤器链的包装类        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);        if (logger.isTraceEnabled()) {            logger.trace("Mapped to " + handler);        }        else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {            logger.debug("Mapped to " + executionChain.getHandler());        }        // 是否是跨域请求,就是查看 request 请求头中是否有 Origin 属性        if (CorsUtils.isCorsRequest(request)) {            CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);        }        return executionChain;    }

进入 getHandlerInternal方法

所在类:org.springframework.web.servlet.handler. AbstractHandlerMethodMapping

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {        // 从request对象中获取 URL,/common/query2        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);        this.mappingRegistry.acquireReadLock();        try {            // 根据 URL 从映射关系中找到对应的 HandlerMethod 对象            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);            // 执行beanFactory.getBean的过程,获取Controller实例            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);        }        finally {            this.mappingRegistry.releaseReadLock();        }    }

::: warning 知识点

lookupHandlerMethod方法之所以可以从映射关系中拿到 HandlerMethod对象,是因为 AbstractHandlerMethodMapping类实现了 InitializingBean接口,在 afterPropertiesSet方法里建立好了映射关系。:::

进入 lookupHandlerMethod方法

所在类:org.springframework.web.servlet.handler. AbstractHandlerMethodMapping

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {        List<Match> matches = new ArrayList<>();        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);        if (directPathMatches != null) {            // 匹配过程,是否符合 RequestMappingInfo 里的属性值            addMatchingMappings(directPathMatches, matches, request);        }        if (matches.isEmpty()) {            // No choice but to go through all mappings...            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);        }        if (!matches.isEmpty()) {            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));            matches.sort(comparator);            Match bestMatch = matches.get(0);            if (matches.size() > 1) {                if (logger.isTraceEnabled()) {                    logger.trace(matches.size() + " matching mappings: " + matches);                }                if (CorsUtils.isPreFlightRequest(request)) {                    return PREFLIGHT_AMBIGUOUS_MATCH;                }                Match secondBestMatch = matches.get(1);                // 如果两个 RequestMappinginfo 什么都相同,报错                if (comparator.compare(bestMatch, secondBestMatch) == 0) {                    Method m1 = bestMatch.handlerMethod.getMethod();                    Method m2 = secondBestMatch.handlerMethod.getMethod();                    String uri = request.getRequestURI();                    throw new IllegalStateException(                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");                }            }            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);            handleMatch(bestMatch.mapping, lookupPath, request);            return bestMatch.handlerMethod;        }        else {            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);        }    }

::: warning 知识点

addMatchingMappings方法,主要一个匹配过程,匹配 @RequestMapping注解中的属性值是否满足

 /*  * consumes:指定处理请求的提交内容类型(Content-Type),  *     例如application/json, text/html;  * produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;  * params:指定request中必须包含某些参数值是,才让该方法处理。  * headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。  * */@RequestMapping(value = "/getUser",    method = RequestMethod.GET,    params = "username=jack",    consumes = "application/json",    produces = "application/json",    headers = "Referer=http://www.xx.com/")

:::

返回 getHandler,进入 getHandlerExecutionChain方法

所在类:org.springframework.web.servlet.handler. AbstractHandlerMapping

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {        // 如果没有获得则创建一个 HandlerExecutionChain        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));        // 获取当前的请求地址:/user/xxx        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);        // 在 HandlerExecutionChain 中添加拦截器        // 遍历 SpringMVC 容器的所有拦截器        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {            // 判断拦截器类型,如果是 MappedInterceptor 类型            if (interceptor instanceof MappedInterceptor) {                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;                // 则先匹配路径后再添加到执行链                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {                    chain.addInterceptor(mappedInterceptor.getInterceptor());                }            }            else {                // 否则直接添加到执行链                chain.addInterceptor(interceptor);            }        }        return chain;    }

::: warning 知识点

getHandlerExecutionChain中的 HandlerInterceptor拦截器是 SpringMVC中的, SpringAOP中的拦截器是 MethodInterceptor。:::

拿到当前请求对应的 handler后,

返回主流程,进入 getHandlerAdapter方法

所在类:org.springframework.web.servlet. DispatcherServlet

/** * TODO : 根据 handlerMethod对象,找到合适的 HandlerAdapter对象,这里用到了策略模式 */protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {        if (this.handlerAdapters != null) {            for (HandlerAdapter adapter : this.handlerAdapters) {                if (adapter.supports(handler)) {                    // 返回一个可以支持的HandlerAdapter 处理程序实例                    return adapter;                }            }        }        throw new ServletException("No adapter for handler [" + handler +                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");    }

::: warning 知识点

HandlerAdapter 是什么HandlerAdapter是一个接口,充当自身与处理程序对象之间的桥梁,从而导致松散耦合设计。HandlerAdapter主要处理方法参数、相关注解、数据绑定、消息转换、返回值、调用视图解析器等。

RequestMappingHandlerMapping为当前的请求找到合适的处理程序方法。

RequestMappingHandlerAdapter执行这个处理程序方法,并为它提供反射调用所需要的参数。

 

HandlerAdapter UML 图

HandlerAdapter的4个实现类:

SimpleServletHandlerAdapter: 适配实现 Servlet 接口的 Handler, 默认调用其 service方法

SimpleControllerHandlerAdapter: 适配实现 Controller 接口的 Handler, 默认调用其 handleRequest 方法

HttpRequestHandlerAdapter: 适配实现 HttpRequestHandler 接口的 Handler, 默认调用其 handleRequest 方法

RequestMappingHandlerAdapter: 适配被 @RequestMapping注释的方式, 一般都是解析一个一个参数, 并且通过反射进行激活

HandlerAdapter 总结HandlerAdapter 是 SpringMVC中扩展机制的非常好的一个体现,,通过 HandlerAdapter这种设计模式, DispatcherServlet 就可以支持任何格式的 Handler(这里的可以支持指在不改变 DispatcherServlet 的情况下),第二是 HandlerAdapter 基于不同 Handler实现不同实现类(策略模式),最后也是最重要的就是参数的解析与返回值的解析。

:::

::: danger 为什么要用HandlerAdapter适配器模式? 首先, Controller的定义有多种 ,一种是带 @Controller注解的, 还可以写一个 servlet 当做 controller, 所以用适配器做适配,不同子类实现 HandlerAdapter接口,定义自己的业务逻辑,每个子类都是适配某一种类型的控制器,有了 HandlerAdapter,你只需要调用自己实现的 handle方法,屏蔽了不一致的细节,对用户来说直接找到对应的处理方法,无须关系哪个实现方法,否则只能在 DispatcherServlet里面通过 if、 else来处理了。:::

前置过滤器

返回主流程,进入 applyPreHandle方法,前置过滤器

所在类:org.springframework.web.servlet. DispatcherServlet

/** * TODO :调用所有的 HandlerInterceptor 拦截器并调用其 preHandler方法 */boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {        // 获取所有的拦截器        HandlerInterceptor[] interceptors = getInterceptors();        if (!ObjectUtils.isEmpty(interceptors)) {            for (int i = 0; i < interceptors.length; i++) {                HandlerInterceptor interceptor = interceptors[i];                // 分别调用拦截器的 preHandle 方法                if (!interceptor.preHandle(request, response, this.handler)) {                    triggerAfterCompletion(request, response, null);                    return false;                }                // 如果失败,记录最后一次拦截器的位置,倒序释放                this.interceptorIndex = i;            }        }        return true;    }

返回主流程,进入 handle方法,调用具体 Controller的方法

最终会进入 AbstractHandlerMethodAdapter的 handle方法,

    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return handleInternal(request, response, (HandlerMethod) handler);    }

进入 handleInternal方法,

所在类:org.springframework.web.servlet.mvc.method.annotation. RequestMappingHandlerAdapter

protected ModelAndView handleInternal(HttpServletRequest request,            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ModelAndView mav;        checkRequest(request);        // Execute invokeHandlerMethod in synchronized block if required.        if (this.synchronizeOnSession) {            HttpSession session = request.getSession(false);            if (session != null) {                Object mutex = WebUtils.getSessionMutex(session);                synchronized (mutex) {                    mav = invokeHandlerMethod(request, response, handlerMethod);                }            }            else {                // No HttpSession available -> no mutex necessary                // 执行 HandlerMethod,返回 ModelAndView                mav = invokeHandlerMethod(request, response, handlerMethod);            }        }        else {            // No synchronization on session demanded at all...            // 执行 HandlerMethod,返回 ModelAndView            mav = invokeHandlerMethod(request, response, handlerMethod);        }        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);            }            else {                prepareResponse(response);            }        }        return mav;    }

进入 invokeHandlerMethod方法,

所在类:org.springframework.web.servlet.mvc.method.annotation. RequestMappingHandlerAdapter

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ServletWebRequest webRequest = new ServletWebRequest(request, response);        try {            // 获取数据绑定工厂  @InitBinder注解支持,            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);            // Model工厂,收集了@ModelAttribute注解的方法            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);            //可调用的方法对象            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);            if (this.argumentResolvers != null) {                //设置参数解析器                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);            }            if (this.returnValueHandlers != null) {                // 设置返回值解析器                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);            }            // 设置参数绑定工厂            invocableMethod.setDataBinderFactory(binderFactory);            // 设置参数名称解析类            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);            ModelAndViewContainer mavContainer = new ModelAndViewContainer();            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));            // 调用有 @ModelAttribute注解的方法。每次请求都会调用有 @ModelAttribute注解的方法            //把 @ModelAttribute注解的方法的返回值存储到 ModelAndViewContainer对象的 map中了            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);            if (asyncManager.hasConcurrentResult()) {                Object result = asyncManager.getConcurrentResult();                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];                asyncManager.clearConcurrentResult();                LogFormatUtils.traceDebug(logger, traceOn -> {                    String formatted = LogFormatUtils.formatValue(result, !traceOn);                    return "Resume with async result [" + formatted + "]";                });                invocableMethod = invocableMethod.wrapConcurrentResult(result);            }            // Controller方法调用,重点看看            invocableMethod.invokeAndHandle(webRequest, mavContainer);            if (asyncManager.isConcurrentHandlingStarted()) {                return null;            }            return getModelAndView(mavContainer, modelFactory, webRequest);        }        finally {            webRequest.requestCompleted();        }    }

::: warning 知识点

invokeHandlerMethod方法主要进行了数据和参数的绑定、创建 ModelAndViewContainer视图容器,以及相关初始化工作。:::

进入 invokeAndHandle方法

所在类:org.springframework.web.servlet.mvc.method.annotation. ServletInvocableHandlerMethod

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,            Object... providedArgs) throws Exception {        // 具体调用逻辑,重点看        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);        setResponseStatus(webRequest);        if (returnValue == null) {            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {                mavContainer.setRequestHandled(true);                return;            }        }        else if (StringUtils.hasText(getResponseStatusReason())) {            mavContainer.setRequestHandled(true);            return;        }        mavContainer.setRequestHandled(false);        Assert.state(this.returnValueHandlers != null, "No return value handlers");        try {            // 返回值处理            this.returnValueHandlers.handleReturnValue(                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);        }        catch (Exception ex) {            if (logger.isTraceEnabled()) {                logger.trace(formatErrorForReturnValue(returnValue), ex);            }            throw ex;        }    }

进入 invokeForRequest方法

所在类:org.springframework.web.method.support. InvocableHandlerMethod

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,            Object... providedArgs) throws Exception {        // 获取参数数组,重点看        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);        if (logger.isTraceEnabled()) {            logger.trace("Arguments: " + Arrays.toString(args));        }        return doInvoke(args);    }

进入 getMethodArgumentValues方法

所在类:org.springframework.web.method.support. InvocableHandlerMethod

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,            Object... providedArgs) throws Exception {        if (ObjectUtils.isEmpty(getMethodParameters())) {            return EMPTY_ARGS;        }        // 入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息        MethodParameter[] parameters = getMethodParameters();        Object[] args = new Object[parameters.length];        for (int i = 0; i < parameters.length; i++) {            MethodParameter parameter = parameters[i];            // 设置参数名称解析器            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);            args[i] = findProvidedArgument(parameter, providedArgs);            if (args[i] != null) {                continue;            }            // 典型的策略模式,根据 parameter 能否找到对应参数的处理类,能找到就返回true            if (!this.resolvers.supportsParameter(parameter)) {                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));            }            try {                // 具体参数值解析过程,重点看看                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);            }            catch (Exception ex) {                // Leave stack trace for later, exception may actually be resolved and handled..                if (logger.isDebugEnabled()) {                    String error = ex.getMessage();                    if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {                        logger.debug(formatArgumentError(parameter, error));                    }                }                throw ex;            }        }        return args;    }

进入 resolveArgument方法

所在类:org.springframework.web.method.support. HandlerMethodArgumentResolverComposite

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {        // 根据参数获取对应参数的解析类        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);        if (resolver == null) {            throw new IllegalArgumentException(                    "Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +                            " supportsParameter should be called first.");        }        // 策略模式去调用具体参数解析类        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);    }

::: warning 知识点

MethodParameter类是对参数信息的封装,其中重要的几个属性包括:

  • parameterIndex:参数的索引位置
  • Parameter:具体参数的对象,包含参数名字
  • parameterType:参数的类型
  • parameterAnnotations:参数的注解数组,一个参数可以有多个注解
  • parameterName:参数名 等等 :::

方法参数解析

进入 getArgumentResolver方法

所在类:org.springframework.web.method.support. HandlerMethodArgumentResolverComposite

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {        // 先从缓存中拿到参数处理器对象        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);        // 如果缓存中没有        if (result == null) {            // 循环容器中 HandlerMethodArgumentResolver类型的所有解析器: List<HandlerMethodArgumentResolver>            for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {                // 典型的策略模式匹配,拿到当前参数对应的处理解析类                if (methodArgumentResolver.supportsParameter(parameter)) {                    // 赋值给 HandlerMethodArgumentResolver 对象                    result = methodArgumentResolver;                    // 放到缓存中                    this.argumentResolverCache.put(parameter, result);                    break;                }            }        }        return result;    }

::: warning 知识点

HandlerMethodArgumentResolver参数解析器,最复杂的处理流程之一,Spring中默认有26种参数解析器,来对应完成某种参数的解析工作。添加过程是 SpringMVC启动实例化后,通过 RequestMappingHandlerAdapter类的 afterPropertiesSet方法调用 getDefaultArgumentResolvers添加到 HandlerMethodArgumentResolver解析器中的。:::

中置过滤器

返回主流程,进入 handleInternal方法,中置过滤器

所在类:org.springframework.web.servlet.mvc.method.annotation. RequestMappingHandlerAdapter

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)            throws Exception {        // 获取所有拦截器        HandlerInterceptor[] interceptors = getInterceptors();        if (!ObjectUtils.isEmpty(interceptors)) {            for (int i = interceptors.length - 1; i >= 0; i--) {                HandlerInterceptor interceptor = interceptors[i];                // 分别调用拦截器的 postHandle方法                interceptor.postHandle(request, response, this.handler, mv);            }        }    }

::: warning 知识点中置过滤器的调用时序,是当 ha.handle 掉完以后,也就是 Controller 里面具体方法调用完以后才轮到中置过滤器调用。可以根据 ModelAndView对象做视图修改。:::

后置过滤器

返回主流程,进入 triggerAfterCompletion方法,后置过滤器

所在类:org.springframework.web.servlet.mvc.method.annotation. RequestMappingHandlerAdapter

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)            throws Exception {        HandlerInterceptor[] interceptors = getInterceptors();        if (!ObjectUtils.isEmpty(interceptors)) {            for (int i = this.interceptorIndex; i >= 0; i--) {                HandlerInterceptor interceptor = interceptors[i];                try {                    interceptor.afterCompletion(request, response, this.handler, ex);                }                catch (Throwable ex2) {                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);                }            }        }    }

::: warning 知识点

applyPreHandle前置过滤器主要作用是对当前请求做一些初步的验证,内部执行顺序是正序遍历, applyPostHandle 中置过滤器和 triggerAfterCompletion后置过滤器的执行顺序是倒序遍历,倒序是因为一但前置过滤器中断(拦截器 preHndle返回 false)那么请求终止,根据中断的数组坐标 interceptorIndex,倒序释放执行已经过滤的的拦截方法。:::

到此为止,主流程结束,以上完成了Spring MVC`从请求到处理的一系列过程,包括请求方法调用

、参数解析、过滤器调用等,接下来就是处理返回值的解析工作。

附:常见注解

::: danger 常见注解

  • @ModelAttribute:会在调用Controller的每个方法执行前被执行。
  • @RequestBody:用来处理 content-type为 application/json的类型,可以是对象。
  • @ResponseBody:一般用于返回 JSON 或 XML 数据。
  • @RequestPart:用来处理 content-type为 multipart/form-data类型的表单提交请求。
  • @ExceptionHandler:用在方法上,在运行时有效,只捕获当前Controller 中发生的异常。
  • @ControllerAdvice:用在类上,@ControllerAdvice("com.xx.xx")只对这个包里 面的 Controller 生效,并将该类中所有使用了 @ExceptionHandler 注解的方法都应用到请求处理方法上。
  • @Cacheable:若该缓存中没有存储该条记录,则执行该方法,有则从缓存取。
  • @CacheEvict:将该缓存下的所有记录都清空。
  • @CachePut:总是会执行该方法,每次都把返回结果更新进该缓存中。
  • @RequestParam:适用于所有类型的参数;
  • @RequestHeader:用于将请求头的信息数据映射到方法参数上 。
  • @CookieValue:用于将请求的 cookie 数据映射到功能处理方法的参数上。
  • @InitBinder:用于绑定表单数据的注解。
  • @RequestAttribute:用于获取 request作用域 中的数据。
  • @SessionAttribute:用于获取 session作用域中的数据。
  • @PathVariable:获取请求 URL 中的动态参数(路径参数),如:"/test4/{id}/{name}"。
  • @MatrixVariable:扩展了URL请求地址,多个请求参数可用 ,分开, 一般用于进行多条件的组合查询。
  • @CrossOrigin:用于不同域名访问,解决跨域问题。


Tags:Spring MVC   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
想要了解Spring MVC框架的原理,探究框架是如何设计的,不错的学习方式是阅读源码,然后自己手写一个框架。本文带领大家简化的手写一个Spring MVC框架。Spring框架对于Java后端程...【详细内容】
2021-09-22  Tags: Spring MVC  点击:(52)  评论:(0)  加入收藏
现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了...【详细内容】
2021-05-27  Tags: Spring MVC  点击:(195)  评论:(0)  加入收藏
在使用Spring MVC的时候,标准的配置是如下这样的: 注意注意:小编整理了一份Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC,有需要的朋友可以私信“spring”免费...【详细内容】
2021-04-13  Tags: Spring MVC  点击:(239)  评论:(0)  加入收藏
DispatcherServlet作为Spring MVC的核心控制器,初始化组件,处理客户端发送的请求,并返回 ModelAndView,进行视图渲染。主要是实现了父类 FrameworkServlet的抽象方法 doService()。...【详细内容】
2020-10-10  Tags: Spring MVC  点击:(71)  评论:(0)  加入收藏
通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求。然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序...【详细内容】
2020-03-11  Tags: Spring MVC  点击:(47)  评论:(0)  加入收藏
在本例中,我们将使用Spring MVC框架构建一个入门级web应用程序。Spring MVC 是Spring框架最重要的的模块之一。它以强大的Spring IoC容器为基础,并充分利用容器的特性来简化...【详细内容】
2019-08-07  Tags: Spring MVC  点击:(222)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(11)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(26)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(55)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条