SpringMVC-請求參數(shù)綁定原理

  • 控制層框架歷史:

Servlet->Structs1.0->Structs2.0->SpringMVC->SpringBoot(嵌入式)

  • 其實控制層框架包含兩個層面的綁定:路由地址的綁定(涉及控制器及具體路由方法綁定)、請求參數(shù)的綁定戒突,本文講的是后者 后續(xù)有時間會同步前者
  • 1. 你知道的參數(shù)綁定注解有哪些屯碴?

所屬jar:org.springframework:spring-web:5.2.12.RELEASE


org.springframework:spring-web:5.2.12.RELEASE

所屬package:package org.springframework.web.bind.annotation


package org.springframework.web.bind.annotation

標注有@Target(ElementType.PARAMETER)注解的均屬于參數(shù)綁定注解如下:

@CookieValue(綁定客戶端的cookie屬性)
@MatrixVariable(是對@PathVariable的增強)
@ModelAttribute
@PathVariable(路徑參數(shù)綁定)
@RequestAttribute(服務端自身參數(shù)綁定)
@RequestBody(請求體json或者xml參數(shù)綁定)
@RequestHeader(請求頭參數(shù)綁定)
@RequestParam(param參數(shù)綁定)
@RequestPart(文件上傳參數(shù)綁定)
@SessionAttribute(session屬性綁定)

@SessionAttributes(session屬性綁定) 作用域類上

  • 2. 簡單介紹一下上面注解的使用方法 參考:ParamBindController.java

涉及的相關代碼如下:

public class RequestAttributeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("username", "小喇叭");
        //設置session屬性
        HttpSession session = request.getSession();
        session.setAttribute("age", 100);
        return true;
    }
}

/**
 * Spring 3.2就已經支持@MatrixVariable特性,但直至現(xiàn)在其依然為默認禁用的狀態(tài)
 *
 * @author lvsheng
 * @version 1.0.0
 * @date 2022/03/08 13:14
 * @see WebMvcConfigurer
 */
@Configuration
public class MatrixVariableConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        WebMvcConfigurer.super.addInterceptors(registry);
        registry.addInterceptor(new RequestAttributeInterceptor()).addPathPatterns("/**/request-attribute", "/**/session-attribute");
    }
}

package com.oceanus.bind.sharing;

import cn.hutool.core.util.StrUtil;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Controller
@SessionAttributes(types = {UserDto.class}, value = "userDto")
@RequestMapping("/param")
public class ParamBindController {

    /**
     * 響應
     */
    @Resource
    private HttpServletResponse response;

    /**
     * 請求
     */
    @Resource
    private HttpServletRequest request;

//    /**
//     * 預請求:
//     *
//     * 被@ModelAttribute注釋的方法會在這個控制器每個方法執(zhí)行前被執(zhí)行
//     *
//     * @author lvsheng
//     * @date 2022/03/08 15:54
//     */
//    @ModelAttribute
//    public void preRequest() {
//        preRequestForRequestAttribute();
//        preRequestForSessionAttribute();
//    }


    /***
     * 預請求-cookie值
     *
     * @author lvsheng
     * @date 2022/03/08 10:56
     */
    @ResponseBody
    @GetMapping("/pre-request/cookie-value")
    public void preRequestForCookieValue() {
        Cookie cookie = new Cookie("username", "lvsheng");
        response.addCookie(cookie);
    }

    /**
     * 預請求-request-attribute請求轉發(fā)
     *
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:32
     */
    @GetMapping("/pre-request/request-attribute")
    public String preRequestForRequestAttribute() {
        //設置服務端系統(tǒng)屬性參數(shù)
        request.setAttribute("username", "小麻花");
        return "forward:/param/request-attribute";
    }

    /**
     * 預請求-session-attribute請求轉發(fā)
     *
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:32
     */
    @GetMapping("/pre-request/session-attribute")
    public String preRequestForSessionAttribute() {
        //設置session屬性
        HttpSession session = request.getSession();
        session.setAttribute("age", 100);
        return "forward:/param/session-attribute";
    }

    /***
     * cookie值
     * Since:
     * 3.0
     *
     * 操作步驟:
     *
     * STEP1: http://localhost:8041/param/pre-request/cookie-value
     * STEP2: http://localhost:8041/param/cookie-value
     *
     * @param username 用戶名稱
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 10:56
     */
    @ResponseBody
    @GetMapping("/cookie-value")
    public String cookieValue(@CookieValue(value = "username", defaultValue = "xmz") String username) {
        return "username=" + username;
    }


    /***
     * 矩陣變量-單個屬性接收
     *
     * Spring 3.2就已經支持@MatrixVariable特性膊存,但直至現(xiàn)在其依然為默認禁用的狀態(tài)
     * 是為了增強URL請求地址的功能 需要配置removeSemicolonContent屬性值為false才生效
     * com.compass.msg.web.core.config.MatrixVariableConfig#configurePathMatch(org.springframework.web.servlet.config.annotation.PathMatchConfigurer)
     *
     * 用法: ;是用來分割變量的 ,是用來分割多個值的(多個值也可以使用多個相同的變量命名)
     *
     * 測試:
     * http://localhost:8041/param/matrix-variable/attribute/username=lvsheng;age=100
     *
     * @param username 用戶名
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 11:05
     */
    @ResponseBody
    @GetMapping("/matrix-variable/attribute/{variable}")
    public String matrixVariableAttribute(@MatrixVariable(value = "username", defaultValue = "xmz") String username,
            @MatrixVariable("age") Integer age) {
        return "username=" + username + ";age=" + age;
    }

    /***
     * 矩陣變量-Map接收
     *
     * 測試:
     * http://localhost:8041/param/matrix-variable/attribute/username=lvsheng;age=100
     *
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 13:46
     */
    @ResponseBody
    @GetMapping("/matrix-variable/map/{variable}")
    public String matrixVariableMap(@MatrixVariable Map<String, Object> map) {
        return "username=" + map.get("username") + ";age=" + map.get("age");
    }

    /***
     * 矩陣變量-List接收
     *
     * 測試:
     * http://localhost:8041/param/matrix-variable/list/address=上海,蘇州,昆山
     * http://localhost:8041/param/matrix-variable/list/address=上海;address=蘇州;address=昆山
     *
     * @param addressList 地址列表
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 13:24
     */
    @ResponseBody
    @GetMapping("/matrix-variable/list/{variable}")
    public String matrixVariableList(@MatrixVariable("address") List<String> addressList) {
        return String.join(StrUtil.COMMA, addressList);
    }

    /***
     * 矩陣變量-數(shù)組接收
     *
     * 測試:
     * http://localhost:8041/param/matrix-variable/arr/address=上海,蘇州,昆山
     * http://localhost:8041/param/matrix-variable/arr/address=上海;address=蘇州;address=昆山
     *
     * @param addressArr 地址數(shù)組
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 13:24
     */
    @ResponseBody
    @GetMapping("/matrix-variable/arr/{variable}")
    public String matrixVariableArr(@MatrixVariable("address") String[] addressArr) {
        return String.join(StrUtil.COMMA, addressArr);
    }

    /***
     * 矩陣變量的路徑PathVariable及MatrixVariable搭配使用
     *
     * 測試:
     * http://localhost:8041/param/matrix-variable/path/lvsheng;address=上海
     *
     * @param username 用戶名
     * @param address 地址
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 13:40
     */
    @ResponseBody
    @GetMapping("/matrix-variable/path/{username}")
    public String matrixVariablePath(@PathVariable("username") String username, @MatrixVariable("address") String address) {
        return "username=" + username + ";address=" + address;
    }


    /***
     * 模型屬性
     * Since:
     * 2.5
     *
     * 測試:
     * http://localhost:8041/param/model-attribute?username=lvsheng&age=100&address=山海
     *
     * @param userDto 用戶
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:00
     */
    @ResponseBody
    @GetMapping("/model-attribute")
    public String modelAttribute(@ModelAttribute UserDto userDto) {
        return "username=" + userDto.getUsername() + ";age=" + userDto.getAge() + ";address=" + userDto.getAddress();
    }


    /***
     * 路徑變量
     * Since:
     * 3.0
     * 測試:
     * http://localhost:8041/param/path-variable/你好
     *
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:02
     */
    @ResponseBody
    @GetMapping("/path-variable/{info}")
    public String pathVariable(@PathVariable String info) {
        return info;
    }

    /***
     * 請求屬性 Since: 4.3
     *
     * 接收服務端設置的屬性值 并非客戶端(前端傳遞)的屬性值-@RequestParam等其他注解均是客戶端參數(shù)
     *
     * 測試:
     * WAY1: http://localhost:8041/param/pre-request/request-attribute(請求轉發(fā))
     *
     * WAY2: http://localhost:8041/param/request-attribute (攔截器操作)
     *
     * WAY3: 將preRequest注釋代碼打開(@ModelAttribute預存值)
     *
     * @param username 用戶名
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:04
     */
    @ResponseBody
    @GetMapping("/request-attribute")
    public String requestAttribute(@RequestAttribute(required = false) String username) {
        return "username=" + username;
    }


    /***
     *
     * 請求體
     *
     * Since:
     * 3.0
     *
     * 測試:
     *
     * curl --location --request GET 'http://localhost:8041/param/request-body' \
     * --header 'Content-Type: application/json' \
     * --header 'Cookie: username=lvsheng' \
     * --data-raw '{
     *     "username": "呂升",
     *     "age": 100,
     *     "address": "上海"
     * }'
     *
     * @param userDto 用戶
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:50
     */
    @ResponseBody
    @GetMapping("/request-body")
    public String requestBody(@RequestBody UserDto userDto) {
        return userDto.toString();
    }

    /***
     * 請求頭
     * Since:
     * 3.0
     *
     * 測試:
     *
     * curl --location --request GET 'http://localhost:8041/param/request-header' \
     * --header 'username: zhangsan' \
     * --header 'Cookie: username=lvsheng'
     *
     * @param username 用戶名
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:52
     */
    @ResponseBody
    @GetMapping("/request-header")
    public String requestHeader(@RequestHeader String username) {
        return "username=" + username;
    }

    /***
     * 請求參數(shù)
     * Since:
     * 2.5
     *
     * 測試:
     *
     * http://localhost:8041/param/request-param?username=lvsheng
     *
     * @param username 用戶名
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 14:53
     */
    @ResponseBody
    @GetMapping("/request-param")
    public String requestParam(@RequestParam String username) {
        return "username=" + username;
    }

    /***
     * 請求的一部分
     * Since:
     * 3.1
     *
     * 測試: curl --location --request GET 'http://localhost:8041/param/request-part' \ --header 'Cookie: username=lvsheng;
     * JSESSIONID=AE346BDC6BB4211C14CBF6A7F84D5B58' \ --form 'upload=@"/C:/Users/sheng.lv/AppData/Local/Postman/app-8.12.4/Squirrel-UpdateSelf.log"'
     *
     * @param multipartFile 文件
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 15:07
     */
    @ResponseBody
    @GetMapping("/request-part")
    public String requestPart(@RequestPart("upload") MultipartFile multipartFile) {
        return multipartFile.getOriginalFilename();
    }

    /***
     * 會話屬性
     * Since:
     * 4.3
     *
     * 測試:
     *
     * WAY1: http://localhost:8041/param/pre-request/session-attribute(請求轉發(fā))
     *
     * WAY2: http://localhost:8041/param/session-attribute(攔截器)
     *
     * WAY3: 將preRequest注釋代碼打開(@ModelAttribute預存值)
     *
     * @param age 年齡
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/08 15:06
     */
    @ResponseBody
    @GetMapping("/session-attribute")
    public String sessionAttribute(@SessionAttribute Integer age) {
        return "age=" + age;
    }

    /**
     * 會話屬性 需要結合@SessionAttributes注解使用 該注解是作用于類上的
     *
     * @param model 模型
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/09 09:09
     */
    @GetMapping("/session-attributes")
    public String sessionAttributes(Model model) {
        UserDto userDto = new UserDto()
                .setUsername("呂升")
                .setAge(100)
                .setAddress("上海");

        // 這步操作結合+@SessionAttributes(types = {UserDto.class}, value = "userDto")會將userDto放置到
        // HttpSession中
        model.addAttribute("userDto", userDto);
        return "forward:/param/session-attributes/get";
    }

    /**
     * 獲取會話屬性
     *
     * @param userDto 用戶請求實體
     * @return {@link String }
     * @author lvsheng
     * @date 2022/03/09 09:09
     */
    @ResponseBody
    @GetMapping("/session-attributes/get")
    public String getSessionAttribute(@SessionAttribute UserDto userDto) {
        return userDto.toString();
    }
}

  • 拓展:


    filter>interceptor>advice>aspect>controller
完整
  • 3. 常用的參數(shù)綁定注解如下:

(1) ModelAttribute
(2) PathVariable
(3) RequestBody
(4) RequestParam

  • 4. 選取部分注解研究一下內部綁定流程:

spring-mvc工作流程

@PathVariable

@RequestBody RequestBody 綁定對象是使用setter方法還是直接屬性賦值导而?
(jackson object-mapper setter賦值)

  • 5. 源碼跟蹤

  • 5.1 @PathVariable參數(shù)綁定的核心流程:

1 ) org.springframework.web.servlet.DispatcherServlet#doDispatch

//調用處理器 在這個步驟中完成參數(shù)綁定操作
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

2 ) 根據(jù)handler的參數(shù)注解定位到對應參數(shù)解析器
3 ) 調用對應參數(shù)解析器的org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveName方法
4 ) 反射調用handler(Method)的invoke方法
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

/**
     * Invoke the handler method with the given argument values.
     */
    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        }
        catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }
  • 關鍵點:
    org.springframework.web.method.HandlerMethod#parameters 參數(shù)初始化過程:
    在實例化SourceController的時候進行參數(shù)初始化


    debug棧
  • HandlerMethodArgumentResolver 初始化的流程:
    step1:
    org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory 向org.springframework.beans.factory.support.DefaultListableBeanFactory#beanDefinitionNames中添加RequestMappingHandlerAdapter

@SpringBootApplication->@EnableAutoConfiguration->spring.factories->org.springframework.boot.autoconfigure.EnableAutoConfiguration->org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration->WebMvcAutoConfiguration$EnableWebMvcConfiguration->

step2:
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization中實例化RequestMappingHandlerAdapter Bean并調用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet初始化HandlerMethodArgumentResolver

  • 注入RequestMappingHandlerAdapter代碼:
@Bean
        @Override
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
                @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
                @Qualifier("mvcConversionService") FormattingConversionService conversionService,
                @Qualifier("mvcValidator") Validator validator) {
            RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
                    conversionService, validator);
            adapter.setIgnoreDefaultModelOnRedirect(
                    this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
            return adapter;
        }
  • RequestMappingHandlerAdapter的afterPropertiesSet方法初始化HandlerMethodArgumentResolver對象列表
    org.springframework.beans.factory.InitializingBean
    其實是維護在HandlerMethodArgumentResolverComposite的argumentResolvers屬性中
    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        initControllerAdviceCache();
        if (this.argumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.initBinderArgumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
    }
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers
/**
     * Return the list of argument resolvers to use including built-in resolvers
     * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
     */
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

        // Annotation-based argument resolution
        resolvers.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 resolution
        resolvers.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 arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));

        return resolvers;
    }
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultInitBinderArgumentResolvers
/**
     * Return the list of argument resolvers to use for {@code @InitBinder}
     * methods including built-in and custom resolvers.
     */
    private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

        // Annotation-based argument resolution
        resolvers.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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());

        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());

        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

        return resolvers;
    }
  • 5.2 @RequestBody參數(shù)綁定的核心流程:

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor


相同映射方法名及參數(shù)名對比緩存值

同@PathVariable相比多了MessageConvert處理

  • 6. 相關:

@RequestParam同RequestAttribute區(qū)別:@RequestParam綁定的是客戶端參數(shù) @RequestAttribute綁定的是服務端參數(shù)(過濾器、攔截器陌僵、Aop等場景設置的參數(shù))
@ModelAttribute和@RequestBody 區(qū)別

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末碗短,一起剝皮案震驚了整個濱河市偎谁,隨后出現(xiàn)的幾起案子搭盾,更是在濱河造成了極大的恐慌鸯隅,老刑警劉巖蝌以,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跟畅,死亡現(xiàn)場離奇詭異徊件,居然都是意外死亡虱痕,警方通過查閱死者的電腦和手機部翘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門新思,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纵刘,你說我怎么就攤上這事彰导。” “怎么了山析?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵秆剪,是天一觀的道長仅讽。 經常有香客問我钾挟,道長,這世上最難降的妖魔是什么徽千? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任双抽,我火速辦了婚禮牍汹,結果婚禮上柬泽,老公的妹妹穿的比我還像新娘。我一直安慰自己钧嘶,他們只是感情好有决,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著新荤,像睡著了一般苛骨。 火紅的嫁衣襯著肌膚如雪苟呐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音笆呆,去河邊找鬼赠幕。 笑死,一個胖子當著我的面吹牛竖慧,可吹牛的內容都是我干的测蘑。 我是一名探鬼主播碳胳,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼挨约,長吁一口氣:“原來是場噩夢啊……” “哼诫惭!你這毒婦竟也來了夕土?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤角溃,失蹤者是張志新(化名)和其女友劉穎减细,沒想到半個月后赢笨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茧妒,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡纸型,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了铸鹰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皂岔。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡躁垛,死狀恐怖教馆,靈堂內的尸體忽然破棺而出土铺,到底是詐尸還是另有隱情,我是刑警寧澤悲敷,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布部宿,位于F島的核電站瓢湃,受9級特大地震影響,放射性物質發(fā)生泄漏涯穷。R本人自食惡果不足惜拷况,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一粟誓、第九天 我趴在偏房一處隱蔽的房頂上張望鹰服。 院中可真熱鬧,春花似錦悲酷、人聲如沸设易。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讼昆。三九已至控淡,卻和暖如春掺炭,著一層夾襖步出監(jiān)牢的瞬間涧狮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留邢滑,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像衬廷,于是被迫代替她去往敵國和親吗跋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容