wordpress 去除頁面標(biāo)題seo點(diǎn)擊軟件手機(jī)
一、知識回顧
我們知道,接口的參數(shù),一般都要配上注解來一起使用。
不同的參數(shù)注解,決定了傳參的方式不同。
為什么會這樣?
如果讓你設(shè)計接口參數(shù)解析,你會怎么做?
首先,我們知道方法參數(shù)是形參。具體的實(shí)參是request
中帶來的。
那么springboot
底層是如何將path中的實(shí)參與接口的形參對應(yīng)上的?
二、源碼解讀
首先,我們知道接口肯定是歸DispatcherServlet
類來管理的,所以,我們直接進(jìn)入這個類
前面的章節(jié),我們已經(jīng)知道,接口入口方法是:org.springframework.web.servlet.DispatcherServlet#doDispatch
所以,我們進(jìn)入這個方法進(jìn)行斷點(diǎn)跟蹤并分析原理。
DispatcherServlet#doDispatch
方法
1、找到對應(yīng)request的Handler
這里的原理解釋在:
SpringBoot2:請求處理原理分析-請求Path與接口的映射關(guān)系(HandlerMapping)
2、找Handler的適配器
mappedHandler.getHandler()
這個已經(jīng)拿到具體的controller
了,返回類型是HandlerMehod
類型
那么,為什么不直接就反射調(diào)用了?還弄個適配器干啥?
我們進(jìn)入getHandlerAdapter
方法
這里的適配器,有4種
我們在看看適配器結(jié)構(gòu)
它是一個接口,有三個方法。
它有以下幾個實(shí)現(xiàn)類
這里我們會發(fā)現(xiàn),實(shí)現(xiàn)類有6個,為什么,handlerAdapters
變量只有4個了?
我們繼續(xù)看源碼,會發(fā)現(xiàn)是通過DispatcherServlet.properties
配置好的。
D:/app/maven/repository/org/springframework/spring-webmvc/5.2.9.RELEASE/spring-webmvc-5.2.9.RELEASE.jar!/org/springframework/web/servlet/DispatcherServlet.properties
繼續(xù)往下看
通過getHandlerAdapter
方法,我們可以看出,Adapters
是通過supports
方法來確定具體哪個適配器來處理。
supports
的具體邏輯就不看了,因?yàn)?#xff0c;每個實(shí)現(xiàn)的adapter
判斷邏輯不通。
也就是說,getHandlerAdapter
方法循環(huán)遍歷四個adapters
,通過adapter
的supports
方法,找到了handler
對應(yīng)的HandlerAdapter
類。
3、通過適配器處理controller接口參數(shù)
我們常用的是@RequestMapping
類型的接口注解。
所以這里,我主要解讀一下org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
這里,我們可以看出ha.handle
是具體的哪個adapter
來實(shí)現(xiàn)的,從而進(jìn)入對應(yīng)的實(shí)現(xiàn)類里進(jìn)行處理。
我這里肯定是進(jìn)入RequestMappingHandlerAdapter
類中看具體邏輯。
3.1、查看RequestMappingHandlerAdapter適配器處理邏輯
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
這里設(shè)置參數(shù)解析器和返回值處理器。
有26個參數(shù)解析器和15個返回值處理器
26個參數(shù)解析器代碼位置:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers
注意ServletModelAttributeMethodProcessor解析器,添加了2次
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());// Custom argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;}
15個返回值處理器代碼位置
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultReturnValueHandlers
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);// Single-purpose return value typeshandlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));handlers.add(new StreamingResponseBodyReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));// Annotation-based return value typeshandlers.add(new ModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));// Multi-purpose return value typeshandlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());// Custom return value typesif (getCustomReturnValueHandlers() != null) {handlers.addAll(getCustomReturnValueHandlers());}// Catch-allif (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));}else {handlers.add(new ModelAttributeMethodProcessor(true));}return handlers;}
執(zhí)行并處理
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
通過這個名字,我們也可以看出,這里invokeForRequest
就是已經(jīng)處理完請求了。
進(jìn)入invokeForRequest
關(guān)鍵代碼找到了,getMethodArgumentValues
獲取方法參數(shù)值。
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}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;}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 exMsg = ex.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw ex;}}return args;}
首先,我們看下參數(shù)解析器 規(guī)范
有兩個方法
supportsParameter
用來判斷是否支持解析
resolveArgument
用來進(jìn)行具體的解析操作
這里用了設(shè)計模式中的組合模式
HandlerMethodArgumentResolverComposite
實(shí)現(xiàn)了HandlerMethodArgumentResolver
接口
然后,在該類中循環(huán)遍歷,是否有解析器可以處理當(dāng)前參數(shù),如果有,具體怎么解析。
關(guān)鍵代碼行
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
通過斷點(diǎn),我們可以看出,此時args[i]
被賦值了。
繼續(xù)進(jìn)入this.resolvers.resolveArgument
查看邏輯
99行,獲取形參名,108行才是給形參賦值。
所以,在108行之前,都是解析接口方法形參。
我們在看下resolveName
方法,可以看到,這個方法有很多具體的實(shí)現(xiàn)類。
我這里以PathVariable
為例子。
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveName
可以看出,給形參名的值,是從request
中獲取到的。這樣,形參和實(shí)參就對應(yīng)上了。
那么,這里的request.getAttribute
是如何處理好的,這里沒有說明。
網(wǎng)上說是通過org.springframework.web.util.UrlPathHelper
類實(shí)現(xiàn)的。
具體有興趣的同學(xué)自己去研究一下。
那么,到這里,源碼邏輯就差不多結(jié)束了。
三、邏輯梳理
首先,請求接口進(jìn)入DispatcherServlet#doDispatch
方法
先從handlerMapping
中獲取具體的handler
,即找到具體是哪個controller
來處理請求。
而handlerMapping
類型默認(rèn)配置了5種。
找到對應(yīng)的handler
后,在找到對應(yīng)的handlerAdapter
handlerAdapters
適配器類型默認(rèn)配置了4種。
再然后,通過具體的handlerAdapter
來解析方法參數(shù)
而解析方法參數(shù),用到了參數(shù)解析器argumentResolvers
這里,參數(shù)解析器,配置了26種。
并且用了組合模式+緩存
來優(yōu)化性能。
遍歷循環(huán)參數(shù)解析器,找到對應(yīng)參數(shù)的解析器后
在通過接口方法的形參信息,如參數(shù)類型,參數(shù)名稱,參數(shù)注解等。
進(jìn)行具體的參數(shù)解析,并從request
中獲取實(shí)參值,賦值給形參,完成參數(shù)解析。