SpringBoot Web源码分析(二) 请求处理源码分析

层层递进的关系来分析 SpringBoot 的Web模块源码

1. SpringBoot Web源码分析(一) 静态资源源码分析

2. SpringBoot Web源码分析(二) 请求处理源码分析

3. SpringBoot Web源码分析(三) 响应处理源码分析

4. SpringBoot Web源码分析(四) 视图解析器源码分析

5. SpringBoot Web源码分析(五),拦截器和文件上传

6. SpringBoot Web源码分析(六) 异常处理和Servlet组件

7. SpringBoot Web源码分析(七) 嵌入式服务器和定制化原理

1.2 请求处理

1.2.1 请求参数处理

1. 请求映射
  1. xxxMapping
  2. Rest 风格(使用Http请求的方式动词来表示对资源的操作)
    1. 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
    2. 现在:/user get获取 Delete 删除用户 PUT 修改用户 POST 保存用户
  3. 核心 Filter HiddenHttpMethodFilter

开启这个过滤器

  mvc:
    hiddenmethod:
      filter:
        enabled: true
2. Rest原理
  1. 表单提交的时候会带上_method=PUT
  2. 请求过来HidderHttpMethodFilter拦截
    1. 请求是否正常,并且判断是否是 POST
      1. 获取到_method 属性的值
      2. 兼容以下请求,PUT DELETE PATCH
      3. 原生request包装模式 requestWrapper重写了getMethod 方法
      4. 过滤器使用 wrapper。以后的方法调用getMethod是调用requestWrapper的
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest requestToUse = request;
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                if (ALLOWED_METHODS.contains(method)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                }
            }
        }

1.2.2 请求映射处理源码分析

FrameworkServlet

继承树结构:DispatcherServlet 继承 FrameworkServlet 继承 HttpServletBean 继承 HttpServlet

我们都知道,当一个请求发送给服务器时,服务器底层会找到发送请求的地址,并执行对应的Servlet 方法,在HttpServlet类中,将 四种请求的方法封装成为四种do* 方法,我们就从这里下手,这里就是处理请求的入口。

FrameworkServlet 继承了HttpServlet,查找FrameworkServlet中重写的doGet和doPost方法等等

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

    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

    protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

    protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

我们发现这么多的 do 方法中,都是在执行这个 processRequest 方法

FrameworkServlet - processRequest()

进入到 processRequest方法中

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 初始化的过程
        long startTime = System.currentTimeMillis();
        Throwable 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());
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            // 执行核心方法 doService
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

通过查看 processRequest 方法,我们发现此方法做了大量的初始化,并且执行了另一个核心方法 doService 方法

FrameworkServlet - doService()

查看这个doService方法,发现是一个抽象方法, 下层来实现的

protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
DispatcherServlet
DispatchServlet - doService() 实现

于是我们来到了DispatcherServlet这个类中,果然发现了doService的实现方法

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();

            label95:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label95;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.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());
        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }

            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() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

我们发现这个方法的结构和processRequest 方法的结构大体相同,先做出初始化,然后去调用了另一个核心方法 doDispatch方法

通过DispatcherServlet中重写的doService方法,我们可以看出是调用了doDispatch这个方法,到这里我们就能够知道,每次请求都会调用这个核心方法

每一个请求进来都会调动这个方法

DispatchServlet - doDispatch()
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 封装一下Request请求
        HttpServletRequest processedRequest = request;
        // 初始化HandlerExecutionChain
        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);
                // 寻找到Request对应哪一个Hander(controller)方法
                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) {
                // 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) {
            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);
                }
            }
        }
    }
DispatchServlet - getHandler()

在方法的执行过程中我们注意到,有这样一个方法,叫handlerMapping 方法,这个方法的作用就是根据请求找到对应的映射,得到相应的 Handler(Controller)来处理这个请求

寻找handlerMappings的方法

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

我们通过 Debug 方式来查看这个,发现SpringBoot中给有五中HandlerMapping
在这里插入图片描述
再来分析 getHandler 方法,我们可以得出在寻找相关的Handler时,最先寻找的是RequestMappingHandlerMapping,即我们编写的Controller的优先级是最高的,这也解释了我在前面所讲的Controller的优先级是最高的。

在HandlerMapping中我们可以找到我们在Controller层中编写的控制器,也进一步证明了我的猜想
在这里插入图片描述

解释了默认情况下的WelcomePageHandlerMapping,视图默认重定向index.html,这里解释了默认情况下的静态资源的index.html 作为欢迎页面

在这里插入图片描述

小结

所有的请求映射都在HandlerMapping中

  • springboot自动配置欢迎页的HandlerMapping,访问/能访问到index.html
  • springboot自动配置了RequestMappingHandlerMapping
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求
    • 如果有,就找到这个请求的Hander
    • 如果没有,就继续找
  • 需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping

1.2.3 常用参数注解

@RequestMapping("/car/{id}/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String username,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String user_Agent,
                                     @RequestHeader Map<String,String> head,
                                     @RequestParam("age") Integer age,
                                     @RequestParam Map<String,String> param,
                                     @CookieValue Cookie cookie){}

1.2.4 参数处理原理

  • 从前面的分析我们可以得出是HandlerMapping中找到能处理请求的Handler(Controller)
  • 下一步就是为当前 Handler找一个适配器 HandlerAdapter;RequestMappingHandlerAdapter
  • 适配器执行目标方法并且确定方法参数的每一个值
  • 找到参数解析器
  • 返回值处理器
1. 执行过程
// Determine handler adapter for the current request.
// 获取当前 Handler 的适配器
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.
// 真正的执行这个 Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
2. HandlerAdapter 接口
public interface HandlerAdapter {

	boolean supports(Object handler);

	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	long getLastModified(HttpServletRequest request, Object handler);

}

通过这个接口我们可以自定义一个HandlerAdapter实现类,通过supports 来告诉 SpringMVC 这个实现类支持哪一种Handler(也可以自定义),如果支持,就会调用这个接口的Handle方法

SpringBoot 给我们提供了四种 HandlerAdapter

在这里插入图片描述

  • RequestMappingHandlerAdapter 支持方法上标注 @RequestMapping
  • HandlerFunctionAdapter 支持函数式编程

    默认寻找的是第一个
3. 执行目标方法 ha.handle()
// Actually invoke the handle 
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

进入后发现执行 HandleInternal

return handleInternal(request, response, (HandlerMethod) handler);

再深入发现进入了RequestMappingHandlerAdapter类中的checkRequest方法这里也验证了我前面所说的RequestMappingHandlerAdapter 是用来处理这个请求的

checkRequest(request);

继续执行

		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
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

我们发现了一个方法叫做 invokeHandlerMethod (执行目标方法)我们进入

	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			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));
			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);
			}

			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
4. 参数解析器 argumentResolvers

确定将要执行的目标方法的每一个参数值是什么,源码在解析参数的过程中利用了一个for循环来比对参数解析器,如果发现该参数没有对应的参数解析器就会发生保存。并且将对应的参数解析器放入缓存中,防止每次都进行一一比对。

SpringMVC 目标方法能解析多少类型接口,是取决于参数解析器

参数解析器接口

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);

    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}

support方法来判断是否支持当前参数,如果支持就调用resolveArgument方法

5. 返回值处理器 returnValueHandlers

用来定义返回值的类型

6. invokeAndHandle()

封装完成后执行 invokeAndHandle 方法
在这里插入图片描述
通过打断点的方式我们知道,执行了invokeAndHandle 方法后,执行 invokeForRequest 方法,断点就会直接到 Controller中去,所以 invokeForRequest 方法中就是最终的方法

7. invokeForRequest()

真正执行Controller 的方法
在这里插入图片描述

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    	// 确定所有参数,将所有参数获取在 args 这个数组中,调用Controller中的方法时传入这个参数
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
		// 通过反射来执行我们 Controller 中的方法
        return this.doInvoke(args);
    }

出现了反射工具类 ReflectionUtils 进一步验证通过反射来执行 Controller 中的方法
在这里插入图片描述

8. Servlet API

HttpServletRequest,HttpServletResponse 等

ServletRequestMethodArgumentResolver 以上部分参数

@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return (WebRequest.class.isAssignableFrom(paramType) ||
				ServletRequest.class.isAssignableFrom(paramType) ||
				MultipartRequest.class.isAssignableFrom(paramType) ||
				HttpSession.class.isAssignableFrom(paramType) ||
				(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
				Principal.class.isAssignableFrom(paramType) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType) ||
				HttpMethod.class == paramType ||
				Locale.class == paramType ||
				TimeZone.class == paramType ||
				ZoneId.class == paramType);
	}
9. 复杂参数 Map和Model

Map 和 Model 中的参数会自动送到请求域中。

Map 和 Model 会返回 mavContainer.getModel() ----> BindingAwareModelMap() 用来解析Model 和Map

10.自定义参数对象执行过程

自动转换成复杂对象,并且能够联级封装

ServletModelAttributeMethodProcessor 这个参数处理器支持处理自定义参数对象

判断是否是简单参数

public static boolean isSimpleValueType(Class<?> type) {
		return (Void.class != type && void.class != type &&
				(ClassUtils.isPrimitiveOrWrapper(type) ||
				Enum.class.isAssignableFrom(type) ||
				CharSequence.class.isAssignableFrom(type) ||
				Number.class.isAssignableFrom(type) ||
				Date.class.isAssignableFrom(type) ||
				Temporal.class.isAssignableFrom(type) ||
				URI.class == type ||
				URL.class == type ||
				Locale.class == type ||
				Class.class == type));
	}
@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
		Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

		String name = ModelFactory.getNameForParameter(parameter);
		ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
		if (ann != null) {
			mavContainer.setBinding(name, ann.binding());
		}

		Object attribute = null;
		BindingResult bindingResult = null;

		if (mavContainer.containsAttribute(name)) {
			attribute = mavContainer.getModel().get(name);
		}
		else {
			// Create attribute instance
			try {
				attribute = createAttribute(name, parameter, binderFactory, webRequest);
			}
			catch (BindException ex) {
				if (isBindExceptionRequired(parameter)) {
					// No BindingResult parameter -> fail with BindException
					throw ex;
				}
				// Otherwise, expose null/empty value and associated BindingResult
				if (parameter.getParameterType() == Optional.class) {
					attribute = Optional.empty();
				}
				bindingResult = ex.getBindingResult();
			}
		}

		if (bindingResult == null) {
			// Bean property binding and validation;
			// skipped in case of binding failure on construction.
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
					bindRequestParameters(binder, webRequest);
				}
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
			// Value type adaptation, also covering java.util.Optional
			if (!parameter.getParameterType().isInstance(attribute)) {
				attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
			}
			bindingResult = binder.getBindingResult();
		}

		// Add resolved attribute and BindingResult at the end of the model
		Map<String, Object> bindingResultModel = bindingResult.getModel();
		mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);

		return attribute;
	}

web数据绑定器,将请求参数的指定值绑定到 javabean 中

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);

webDataBinder 利用它里面的转换器,将请求的数据转换成指定类型,再封装到 JavaBean 中

GenericConversionService:在设置每一个值的时候,找里面的所有Conversion,哪一个能将这个数据类型转换到指定的类型

在这里插入图片描述

在这里插入图片描述
我们也能够给WebDataBinder 中放我们自己的 Convert


11.自定义Convert
    //1、WebMvcConfigurer定制化SpringMVC的功能
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                // 不移除;后面的内容。矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {

                    @Override
                    public Pet convert(String source) {
                        // 啊猫,3
                        if(!StringUtils.isEmpty(source)){
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
        };
    }
12.总结:参数处理原理

1. HandlerAdapter
2. 执行目标方法
3. 参数解析器HandlerMethodArgumentResolve
4. 返回值处理器
5. 确定目标方法的每一个参数
1. 挨个判断所有参数解析器哪一个支持这个参数
2. 解析这个参数的值
3. 自定义类型参数,封装POJO
6. 目标方法执行完成
7. 处理派发结果

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_CX_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值