日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

SpringBoot各类型参数解析原理(源码)

發(fā)布時(shí)間:2025/3/15 javascript 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot各类型参数解析原理(源码) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上次那篇我們只分析了doDispatch中的getHandler方法(獲取執(zhí)行鏈,執(zhí)行鏈里包括當(dāng)前請(qǐng)求URL對(duì)應(yīng)的 handler 以及攔截器(Controller、method綁定關(guān)系)),今兒繼續(xù)向下看getHandlerAdapter方法和handle方法

public class DispatcherServlet{protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null) {this.noHandlerFound(processedRequest, response);return;}//繼續(xù)向下// Determine handler adapter for the current request.HandlerAdapter ha = 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 ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//開(kāi)始真正處理請(qǐng)求的方法mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);} }

一、getHandlerAdapter

有沒(méi)有想過(guò),為何我們加了那些注解,例如@PathVariable,為什么springmvc就能將其變量確定為對(duì)應(yīng)的值呢?這就是HandlerAdapter的作用,在getHandler方法確定好控制器和對(duì)應(yīng)的方法后(執(zhí)行鏈),getHandlerAdapter就會(huì)來(lái)幫我們?yōu)楫?dāng)前的handler找一個(gè)adapter然后我們通過(guò)該適配器,就能夠?qū)⒄?qǐng)求的鏈接所帶的參數(shù)給適配上。

看一下DispatcherServlet的doService方法時(shí)序圖:

直接進(jìn)入getHandlerAdapter方法查看,debug -getHandlerAdapter方法,可以看到,會(huì)在原生的4種handlerAdapter中選擇一個(gè)匹配的適配器進(jìn)行返回。獲取代碼:

public class DispatcherServlet{protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");} }


對(duì)應(yīng)的處理如下:

注意:如果自己添加了Adapter就不會(huì)在加載springMVC默認(rèn)的這些Adapter

getHandlerAdapter里調(diào)用了adapter.supports(handler)

  • 通過(guò)supports方法來(lái)確定adapter,我們進(jìn)入supports方法,發(fā)現(xiàn)不同的adapter有不同的判斷方法,我們還是先以requestMapping請(qǐng)求的到的handler為例
  • 可以發(fā)現(xiàn)他的判斷方式很簡(jiǎn)單,就是判斷handler是不是一個(gè)HandlerMethod(在上面匹配的時(shí)候會(huì)根據(jù)不同的情況獲得不同的handler)
public class AbstractHandlerMethodAdapter{public final boolean supports(Object handler) {return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));} }

我們可以通過(guò)debug其他類型的handler可以返現(xiàn)他們的判斷方式和上面的類似都是instanceof來(lái)判斷的,匹配后返回具體handleradapter通過(guò)getHandler()和getHandlerAdapter()方法得到的執(zhí)行鏈(得到controller中具體的執(zhí)行方法)和適配器(可以解析請(qǐng)求所帶的參數(shù))后,我們就可以來(lái)真正執(zhí)行請(qǐng)求的方法(handle())了。

二、handle

執(zhí)行

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

來(lái)到

進(jìn)一步,進(jìn)入到該抽象類的實(shí)現(xiàn)類RequestMappingHandlerAdapter中(為何是RequestMappingHandlerAdapter),因?yàn)槲业姆椒ㄊ褂昧?#64;RequestMapping,所以就返回這個(gè)Adapter),對(duì)一個(gè)請(qǐng)求方法的所有操作都會(huì)在這里進(jìn)行。RequestMappingHandlerAdapter 部分源碼如下:可以看到,handleInternal執(zhí)行后,會(huì)返回一個(gè)ModelAndView

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 {//最終來(lái)到invokeHandlerMethod這里,才是真正的執(zhí)行handler方法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;}

invokeHandlerMethod方法源碼:

@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);Object result;try {WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = this.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()) {result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(this.logger, (traceOn) -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});invocableMethod = invocableMethod.wrapConcurrentResult(result);}invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);if (!asyncManager.isConcurrentHandlingStarted()) {ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);return var15;}result = null;} finally {webRequest.requestCompleted();}return (ModelAndView)result;}

其中有兩個(gè)變量值得我們研究:argumentResolvers(參數(shù)解析器 26種)和returnValueHandlers(返回值處理器 15種),這兩個(gè)東西就是這篇文章的主題:參數(shù)解析的核心。

  • 參數(shù)解析器(其實(shí)底層是個(gè)接口)
    看一下HandlerMethodArgumentResolver接口的定義:
  • public interface HandlerMethodArgumentResolver {/** * 解析器是否支持當(dāng)前參數(shù) * * @param var1 需要被解析的Controller參數(shù) * @return */boolean supportsParameter(MethodParameter var1);// 將request中的請(qǐng)求參數(shù)解析到當(dāng)前Controller參數(shù)上,在這里進(jìn)行類型轉(zhuǎn)換 Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;}

    該接口作用:當(dāng)前解析器是否支持解析這種參數(shù),支持就調(diào)用 resolveArgument解析最終確定將要執(zhí)行的目標(biāo)方法的每一個(gè)參數(shù)的值是什么SpringMVC目標(biāo)方法能寫多少種參數(shù)類型。取決于參數(shù)解析器,默認(rèn)26種:

  • 返回值處理器
    決定了目標(biāo)方法到底能寫多少種類型的返回值,默認(rèn)15種

    有一個(gè)值得注意的處理器就是RequestResponseBodyMethodHandler,就是我們使用@ResponseBody時(shí),使用的處理器,底層如下:
  • 在將參數(shù)解析器和返回值處理器設(shè)置好后,進(jìn)一步調(diào)用了invokeAndHandle方法,跟蹤該方法,我們來(lái)到:ServletInvocableHandlerMethod類中的```invokeAndHandle方法
    部分源碼:

    public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);this.setResponseStatus(webRequest);if (returnValue == null) {if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {this.disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}} else if (StringUtils.hasText(this.getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}}

    跟蹤invokeForRequest,來(lái)到InvocableHandlerMethod類, invokeForRequest及getMethodArgumentValues(開(kāi)始解析參數(shù)了)源碼

    public class InvocableHandlerMethod extends HandlerMethod {@Nullablepublic Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}return this.doInvoke(args);}//核心方法,獲取參數(shù)值最底層的方法protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {//獲取到方法的所有參數(shù)聲明(例如注解,索引,類型)MethodParameter[] parameters = this.getMethodParameters();//判斷參數(shù)是否為空,為空直接返回,無(wú)須確定任何值if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;} else {Object[] args = new Object[parameters.length];//挨個(gè)遍歷參數(shù)取值for(int i = 0; i < parameters.length; ++i) {MethodParameter parameter = parameters[i];//確定參數(shù)名字parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] == null) {//先判斷當(dāng)前解析器是否支持這種類型,不支持便對(duì)解析器遍歷,直到找到支持的解析器//具體調(diào)用鏈supportsParameter->HandlerMethodArgumentResolverComposite.getArgumentResolver-> 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 var10) {if (logger.isDebugEnabled()) {String exMsg = var10.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw var10;}}}return args;}} }

    獲取到的參數(shù)聲明:

    HandlerMethodArgumentResolverComposite.getArgumentResolver源碼:
    這里可以看到對(duì)我們上面提到的那26種解析器的遍歷,最后會(huì)完全緩存在springboot的本地緩存中

    拿到參數(shù)解析器后,我們就可以來(lái)獲取參數(shù)的值了
    HandlerMethodArgumentResolverComposite.ArgumentResolver源碼:

    @Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");} else {//獲取參數(shù)值并返回return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}}

    resolveArgument最終會(huì)調(diào)用AbstractNamedValueMethodArgumentResolver的各種實(shí)現(xiàn)類如下:

    再配合UrlPathHelper(會(huì)將url中的變量解析出來(lái),放在request的請(qǐng)求域中),最終得到變量值。

    三、對(duì)于傳入的是Servlet API的參數(shù)的處理

    WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId這些都能找到對(duì)應(yīng)的resolver進(jìn)行解析。
    以ServletRequestMethodArgumentResolver為例,它能解析以下的參數(shù),總之,就是進(jìn)行到resolvers.supportsParameter(parameter)這個(gè)方法后,遍歷那26個(gè)參數(shù)解析器,拿到對(duì)應(yīng)的解析器去解析就好了,原理都是一樣的

    @Overridepublic 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);}

    四、復(fù)雜參數(shù)的處理

    復(fù)雜參數(shù)如:Map、Model(map、model里面的數(shù)據(jù)會(huì)被放在request的請(qǐng)求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向攜帶數(shù)據(jù))、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder等

    那么重點(diǎn)就是,他是怎么給request域放數(shù)據(jù)的呢:debug以下方法看一下

  • 首先,解析參數(shù)都是跟上面一樣的步驟,就是進(jìn)行到resolvers.supportsParameter(parameter)這個(gè)方法后,遍歷那26個(gè)參數(shù)解析器,拿到對(duì)應(yīng)的解析器去解析,而對(duì)于map這類型的數(shù)據(jù)來(lái)說(shuō),就是對(duì)應(yīng)的
  • 拿到解析器后,就得找對(duì)應(yīng)的參數(shù)進(jìn)行影射了,對(duì)于上面的使用@requestmapping注解的方法,它會(huì)去url緩存中獲取參數(shù)值,那么map類型的呢?debug后我們發(fā)現(xiàn),它來(lái)到了mavContainer.getModel()這個(gè)方法,準(zhǔn)備獲取模型數(shù)據(jù)。

    而getModel()這個(gè)方法他會(huì)返回一個(gè)ModeMap類型的數(shù)據(jù),源碼如下:
  • public ModelMap getModel() {if (this.useDefaultModel()) {return this.defaultModel;} else {if (this.redirectModel == null) {this.redirectModel = new ModelMap();}return this.redirectModel;}}

    最終,他是返回一個(gè)ModelMap的子類BindingAwareModelMap,BindingAwareModelMap 是Model 也是Map

    繼承樹如下:

    Model參數(shù)類型就調(diào)用另一個(gè)解析器

    debug后發(fā)現(xiàn),居然跟解析Map類型調(diào)用的是一樣的方法,也是來(lái)到了mavContainer.getModel()這個(gè)方法,準(zhǔn)備獲取模型數(shù)據(jù)。我們可以發(fā)現(xiàn),兩者返回的是同一個(gè)BindingAwareModelMap。同時(shí),直接放心讓request,和response對(duì)象也解析好。

    然后我們放行方法,執(zhí)行完invokeForRequest方法,此時(shí),我們知道,對(duì)于請(qǐng)求的處理已經(jīng)完成了,接下來(lái)就是視圖解析了,這里先不討論視圖解析的流程,就研究forward的時(shí)候,spring是如何將數(shù)據(jù)(model)放在請(qǐng)求域中給轉(zhuǎn)發(fā)出去的。

    跟蹤進(jìn)去,我們發(fā)現(xiàn)在處理返回結(jié)果的時(shí)候,也把mavContainer傳進(jìn)去了:

    mavContainer此時(shí)如下:

    handleReturnValue方法:

    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//先獲取返回類型HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());} else {handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}}

    如果你放回的類型是個(gè)字符串,就把字符串設(shè)置成viewName

    此時(shí)的mavContainer(view已經(jīng)為“”forward:/success)

    至此可以得出一個(gè)結(jié)論:方法執(zhí)行完成后,springmvc會(huì)所有的數(shù)據(jù)都放在 ModelAndViewContainer;包含要去的頁(yè)面地址View。還包含Model數(shù)據(jù)。然后進(jìn)一步對(duì)這些數(shù)據(jù)進(jìn)行處理(渲染),會(huì)執(zhí)行以下:

    繼續(xù)跟蹤

    仍然可以看到,還是圍繞著處理mavContainer展開(kāi),ModelFactory里有一個(gè)
    updateBindingResult方法,這是關(guān)鍵,它會(huì)遍歷所有model的值,并根據(jù)綁定策略對(duì)數(shù)據(jù)進(jìn)行封裝

    然后在執(zhí)行:ModelAndView mav=new ModelAndView(....);這一句,即把遍歷到的model數(shù)據(jù)生成一個(gè)ModelAndView。然后再根據(jù)是不是重定向,轉(zhuǎn)發(fā),或者普通處理,再進(jìn)一步對(duì)數(shù)據(jù)進(jìn)行處理

    此時(shí),DispatchServlet的:

    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    執(zhí)行完成,開(kāi)始執(zhí)行DispatchServlet的另一個(gè)方法:

    //完成業(yè)務(wù)處理后的后置處理mappedHandler.applyPostHandle(processedRequest, response, mv);

    開(kāi)始執(zhí)行render()方法。

    涉及兩個(gè)主要方法:

    //處理派發(fā)結(jié)果 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); //渲染合并輸出模型(最關(guān)鍵的核心) renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); @Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.exposeHelpers(request);// Determine the path for the request dispatcher.String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}rd.forward(request, response);}} 暴露模型作為請(qǐng)求域?qū)傩?// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request); //該方法可以看出,底層最終就是通過(guò)最普通的遍歷,將model數(shù)據(jù)重新放入請(qǐng)求域中 protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {//model中的所有數(shù)據(jù)遍歷挨個(gè)放在請(qǐng)求域中model.forEach((name, value) -> {if (value != null) {request.setAttribute(name, value);}else {request.removeAttribute(name);}});}

    五、自定義POJO類型參數(shù)的處理

    跟上面一樣,來(lái)到resolvers.supportsParameter(parameter),處理POJO類型的有兩個(gè)參數(shù)解析器,都是叫:ServletModelAttributeMethodProcessor,但是一個(gè)是處理帶注解的bean,一個(gè)是處理不帶注解的bean。
    判斷時(shí),先判斷參數(shù)是不是簡(jiǎn)單類型


    而自定義對(duì)象,自然就不是簡(jiǎn)單類型
    然后便開(kāi)始執(zhí)行resolveArgument方法。

  • 先看model中有沒(méi)有(getModel().containsAttribute(name);),沒(méi)有就看get域中有沒(méi)有HandlerMapping.uriTemplateVariables的map里面有沒(méi)有,然后看域中有沒(méi)有,沒(méi)有就反射創(chuàng)建
  • 反射創(chuàng)建一個(gè)沒(méi)有初始化的bean
  • 然后通過(guò)WebDatabinder來(lái)bind屬性
    • WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);核心方法,將請(qǐng)求參數(shù)的值綁定到指定的JavaBean里面,WebDataBinder 利用它里面的 Converters將請(qǐng)求數(shù)據(jù)轉(zhuǎn)成指定的數(shù)據(jù)類型。再次封裝到JavaBean中
    • Converters :底層默認(rèn)有124個(gè),如下:

      我們也可以自定義自己的Converters:
    @FunctionalInterface public interface Converter<S, T>

    總結(jié)

    以上是生活随笔為你收集整理的SpringBoot各类型参数解析原理(源码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: youjizz日韩 | 色在线免费观看 | 在线综合av | 国产乱码精品一区二区三区五月婷 | 欧美第二区 | 久草免费资源站 | 欧美色综合天天久久综合精品 | 1区2区3区在线观看 久久久久久久久久久影院 成人网址在线观看 | 亚洲涩综合 | 国产成人精品免高潮费视频 | 暧暧视频在线观看 | 黄色激情四射 | 免看一级a毛片一片成人不卡 | av网站亚洲 | 欧美性理论片在线观看片免费 | 深夜福利视频网站 | 国产女主播视频 | 男女猛烈无遮挡 | 丝袜综合网| 一区二区国产视频 | 黄视频免费在线观看 | 婷婷综合网站 | 国产一区二区三区在线播放无 | 久久9精品区-无套内射无码 | 久久网站av | 日本欧美久久久久免费播放网 | 精品无码久久久久久久久 | 91黄瓜| 成人精品亚洲人成在线 | 狼人伊人干 | 久久婷五月 | 中文字幕乱码免费 | 成人免费毛片aaaaaa片 | 亚洲free性xxxx护士白浆 | 亚洲精品乱码久久久久久蜜桃91 | 爆操巨乳 | 香蕉网站在线观看 | 伊人中文字幕在线 | 成人国产亚洲 | 草草影院一区二区三区 | 3d动漫精品啪啪一区二区三区免费 | 久久久久久一 | 美女露胸无遮挡 | av中文字幕网 | av免费大全 | 性做久久久久久 | 91国内视频 | 亚洲成人生活片 | 成人精品一区日本无码网 | 国产一级做a爱免费视频 | 伊人久久大香线蕉综合75 | 国产又大又粗又长 | 激情二区 | 亚洲一区二区黄色 | 精品一区二区三区视频在线观看 | 中日韩午夜理伦电影免费 | 欧美性另类 | 美女国产网站 | 熊猫电影yy8y全部免费观看 | 肥臀浪妇太爽了快点再快点 | 黄色网址链接 | 色呦呦视频在线 | 国产做受高潮 | 99人人爽 | 亚洲同性gay激情无套 | 韩国一区二区在线观看 | 麻豆av一区二区三区在线观看 | 午夜精品久久久久久久四虎美女版 | 日本人dh亚洲人ⅹxx | 国内性视频 | 91成人观看 | 亚洲精品水蜜桃 | 亚洲欧美日韩国产一区 | 一级高清视频 | 亚洲精品国产片 | 嫩草一二三 | 亚洲国产毛片aaaaa无费看 | 女人下面无遮挡 | 日韩av男人天堂 | 在线日本视频 | 爱福利视频一区 | 黑人精品欧美一区二区蜜桃 | 成人极品| 丝袜美腿一区二区三区 | 久久亚洲精品小早川怜子 | 噼里啪啦免费看 | 亚洲吧 | 老熟妇一区二区三区啪啪 | 亚欧美在线观看 | 草草影院第一页 | 五级黄高潮片90分钟视频 | 黄色激情四射 | 日本久久精品视频 | 色综合天天综合网天天看片 | 中文字幕人妻丝袜乱一区三区 | 美女吞精视频 | 久久99精品国产麻豆婷婷 | 欧美成人三级 | 亚洲啪av永久无码精品放毛片 |