SpringBoot與SpringMVC中參數(shù)傳遞的原理解析
一:普通參數(shù)與基本注解
HandlerMapping中找到能處理請(qǐng)求的Handler(Controller,method())
為當(dāng)前Handler找一個(gè)適配器HandlerAdapter:RequestMappingHandlerAdapter

1.HandlerAdapter

0-支持方法上標(biāo)注@RequestMapping
1-支持函數(shù)式編程的
xxxx
2.執(zhí)行目標(biāo)方法


3.參數(shù)解析器:確定要執(zhí)行的目標(biāo)方法每一個(gè)參數(shù)的值是什么


boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
先判斷是否支持該參數(shù)類型, 如果支持, 就調(diào)用resolveArgument解析方法
4.返回值處理器

5.挨個(gè)判斷所有參數(shù)解析器哪個(gè)支持這個(gè)參數(shù):HandlerMethodArgumentResolver: 把控著支持的方法參數(shù)類型

請(qǐng)求進(jìn)來后, 首先從handlerMapping中查找是否有對(duì)應(yīng)的映射處理, 得到映射適配器Adapter,再通過適配器,查找有哪些方法匹配請(qǐng)求,首先判斷方法名,以及參數(shù)類型是否匹配,首先獲得方法中聲明的參數(shù)名字, 放到數(shù)組里,循環(huán)遍歷27種解析器判斷是否有支持處理對(duì)應(yīng)參數(shù)名字類型的解析器,如果有的話,根據(jù)名字進(jìn)行解析參數(shù),根據(jù)名字獲得域數(shù)據(jù)中的參數(shù), 循環(huán)每個(gè)參數(shù)名字進(jìn)行判斷, 從而為每個(gè)參數(shù)進(jìn)行賦值.
對(duì)于自定義的POJO類參數(shù):
ServletRequestMethodArgumentResolver 這個(gè)解析器用來解析: 是通過主要是通過判斷是否是簡(jiǎn)單類型得到的
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
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));
}
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();
}
else {
attribute = ex.getTarget();
}
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;
}
WebDataBinder binder =binderFactory.createBinder(webRequest,attribute,name)
WebDataBinder:web數(shù)據(jù)綁定器,將請(qǐng)求參數(shù)的值綁定到指定的javaBean里面
WebDataBinder 利用它里面的Converters將請(qǐng)求數(shù)據(jù)轉(zhuǎn)成指定的數(shù)據(jù)類型,通過反射一系列操作,再次封裝到j(luò)avabean中
GenericConversionService:在設(shè)置每一個(gè)值的時(shí)候,找它里面所有的converter哪個(gè)可以將這個(gè)數(shù)據(jù)類型(request帶來參數(shù)的字符串)轉(zhuǎn)換到指定的類型(javabean—某一個(gè)類型)


未來我們可以給WebDataBinder里面放自己的Converter
private static final class StringToNumber implements Converter<String, T> {
converter總接口:
@FunctionalInterface
public interface Converter<S, T> {
//自定義轉(zhuǎn)換器:實(shí)現(xiàn)按照自己的規(guī)則給相應(yīng)對(duì)象賦值
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
if (!StringUtils.isEmpty(source)){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(split[1]);
return pet;
}
return null;
}
});
}
二:復(fù)雜參數(shù)
Map/Model(map/model里面的數(shù)據(jù)會(huì)被放在request的請(qǐng)求域 相當(dāng)于request.setAttribute)/Errors/BindingResult/RedirectAttributes(重定向攜帶數(shù)據(jù))/ServletRespons().SessionStaus.UriComponentsBuilder
6.在上面第五步目標(biāo)方法執(zhí)行完成后:
將所有的數(shù)據(jù)都放在ModelAdnViewContainer;包含要去的頁(yè)面地址View,還包含Model數(shù)據(jù)

7.處理派發(fā)結(jié)果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
在頁(yè)面進(jìn)行響應(yīng)前, 進(jìn)行視圖渲染的時(shí)候:
exposeModelAsRequestAttributes(model, request); 該方法將model中所有參數(shù)都放在請(qǐng)求域數(shù)據(jù)中
protected 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);
}
}
通過循環(huán)遍歷model中的所有數(shù)據(jù)放在請(qǐng)求域中
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
}
else {
request.removeAttribute(name);
}
});
}
不管我們?cè)诜椒ㄐ螀⑽恢梅?Map集合或者M(jìn)olde 最終在底層源碼都是同一個(gè)對(duì)象在mvcContainer容器中進(jìn)行保存
到此這篇關(guān)于SpringBoot與SpringMVC中參數(shù)傳遞的原理的文章就介紹到這了,更多相關(guān)SpringBoot SpringMVC參數(shù)傳遞內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis-plus報(bào)錯(cuò)Property ‘sqlSessionFactory‘ or 
這篇文章主要給大家介紹了MyBatis-plus 報(bào)錯(cuò) Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required的兩種解決方法,如果遇到相同問題的朋友可以參考借鑒一下2023-12-12
關(guān)于maven打包時(shí)的報(bào)錯(cuò): Return code is: 501 , ReasonPhrase:HTTPS Requ
這篇文章主要介紹了關(guān)于maven打包時(shí)的報(bào)錯(cuò): Return code is: 501 , ReasonPhrase:HTTPS Required,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Java實(shí)現(xiàn)直接插入排序與折半插入排序的示例詳解
這篇文章主要為大家詳細(xì)介紹了插入排序中兩個(gè)常見的排序:直接插入排序與折半插入排序。本文用Java語言實(shí)現(xiàn)了這兩個(gè)排序算法,感興趣的可以學(xué)習(xí)一下2022-06-06
如何擴(kuò)展Spring Cache實(shí)現(xiàn)支持多級(jí)緩存
這篇文章主要介紹了如何擴(kuò)展Spring Cache實(shí)現(xiàn)支持多級(jí)緩存,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
基于Java匯總Spock框架Mock靜態(tài)資源經(jīng)驗(yàn)
這篇文章主要介紹了基于Java匯總Spock框架Mock靜態(tài)資源經(jīng)驗(yàn),前面講了?Spock框架Mock對(duì)象、方法經(jīng)驗(yàn)總結(jié),今天分享一下Spock框架中Mock靜態(tài)資源的實(shí)踐經(jīng)驗(yàn)匯總。分成靜態(tài)資源和混合場(chǎng)景,需要的朋友可以參考一下2022-02-02
springboot簡(jiǎn)單實(shí)現(xiàn)單點(diǎn)登錄的示例代碼
本文主要介紹了springboot簡(jiǎn)單實(shí)現(xiàn)單點(diǎn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
基于Socket類以及ServerSocket類的實(shí)例講解
下面小編就為大家?guī)硪黄赟ocket類以及ServerSocket類的實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
解決SpringMVC Controller 接收頁(yè)面?zhèn)鬟f的中文參數(shù)出現(xiàn)亂碼的問題
下面小編就為大家分享一篇解決SpringMVC Controller 接收頁(yè)面?zhèn)鬟f的中文參數(shù)出現(xiàn)亂碼的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-03-03

