【工作篇】再次熟悉SpringMVC 参数绑定

前言

主要现在项目中使用的参数绑定五花八门的,搞得很头大,例如有些用字符串接收日期,用字符串接受数组等等,完全没有利用好 SpringMVC 的优势,这里自己也总结一下,免得到时又要百度谷歌查找。

以下实践的 Spring 版本是:5.2.7.RELEASE

一、SpringMVC 中不同类型的数据绑定

1.1、基础数据类型

  • 默认参数名
  1. // http://localhost:8080/baseType3?a=123
  2. @GetMapping("/baseType")
  3. @ResponseBody
  4. public String baseType(int a) {
  5. return "baseType " + a;
  6. }
  1. // http://localhost:8080/baseType3?b=123
  2. @GetMapping("/baseType3")
  3. @ResponseBody
  4. public String baseType3(@RequestParam(value = "b", required = true) Integer a) {
  5. return "baseType3 " + a;
  6. }
  • 多个参数
  1. // http://localhost:8080/baseType4?age=10&name=Java
  2. @GetMapping("/baseType4")
  3. public String baseType3(@RequestParam Integer age, String name) {
  4. return "baseType4 age:" + age + " name="+name;
  5. }

1.2、 对象类型

超过三个参数及以上,则推荐使用对象来接收传递的参数

  • 定义简单对象接收参数
  1. @Data //这里使用了 lombok 插件
  2. public class User {
  3. Integer id;
  4. String name;
  5. }
  6. // http://localhost:8080/objectType?id=1&name=Java
  7. @GetMapping("/objectType")
  8. public String objectType(User user) {
  9. return "objectType " + user;
  10. }
  • 内嵌对象接收参数
  1. @Data
  2. public class Order {
  3. Integer id;
  4. User user;
  5. }
  6. // http://localhost:8080/objectType2?id=1&user.name=Java&user.id=2
  7. @GetMapping("/objectType2")
  8. public String objectType2(Order order) {
  9. return "objectType2 " + order;
  10. }
  • 使用 DataBinder 解决不同对象,参数名相同覆盖问题
    • 定义对象
  1. @Data
  2. public class Friend {
  3. Integer id;
  4. String name; //与User 对象name 名称冲突
  5. }
  6. @Data
  7. public class User {
  8. Integer id;
  9. String name;
  10. }
  • InitBinder 配置
    在 Controller 中定义,只对当前 Controller 有效,也可以在 @ControllerAdvice 类中全局定义
  1. /**
  2. * 初始化绑定参数user 标识前缀
  3. *
  4. * @param binder
  5. */
  6. @InitBinder("user")
  7. public void initBinderUser(WebDataBinder binder) {
  8. binder.setFieldDefaultPrefix("user.");
  9. }
  10. /**
  11. * 初始化绑定参数friend 标识前缀
  12. *
  13. * @param binder
  14. */
  15. @InitBinder("friend")
  16. public void initBinderFriend(WebDataBinder binder) {
  17. binder.setFieldDefaultPrefix("friend.");
  18. }
  • 编写请求
  1. //http://localhost:8080/objectType3?name=Java name会同时填充到User 和Friend对象上
  2. //http://localhost:8080/objectType3?user.name=Java&friend.name=Python 分别填充数据到各自的对象中去
  3. @GetMapping("/objectType3")
  4. public String objectType3(User user, Friend friend) {
  5. return "objectType3 user" + user + " friend " + friend;
  6. }

1.3、 日期类型

日期类型的参数传递方式比较多,正式项目中建议统一规定日期类型的参数绑定的格式

1.3.1、使用时间戳传递(不是参数绑定方式)
  1. // http://localhost:8080/dateType6?date=1628752881
  2. @GetMapping("/dateType6")
  3. public String dateType5(Long date) {
  4. return "dateType6 date" + new Date(date);
  5. }

1.3.2、使用字符串接收(不是参数绑定方式)
  1. // http://localhost:8080/dateType7?date=2021-08-12
  2. @GetMapping("/dateType7")
  3. public String dateType7(String date) throws ParseException {
  4. return "dateType7 date" + new SimpleDateFormat("yyyy-MM-dd").parse(date);
  5. }

1.3.3、使用 SpringMVC 默认提供的 @DateTimeFormat (对于 json 参数无效)
  1. // http://localhost:8080/dateType2?date1=2020-01-01
  2. @GetMapping("/dateType2")
  3. public String dateType2(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1) {
  4. return "dateType2 date " + date1;
  5. }

1.3.4、使用 @InitBinder 注册转换器
  • 添加转换器
  1. /**
  2. * 注册日期转换 date
  3. *
  4. * @param binder
  5. */
  6. @InitBinder
  7. public void initBinderDate(WebDataBinder binder) {
  8. binder.addCustomFormatter(new Formatter<Date>() {
  9. @Override
  10. public Date parse(String text, Locale locale) throws ParseException {
  11. System.out.println("InitBinder addCustomFormatter String to Date ");
  12. return new SimpleDateFormat("yyyy-MM-dd").parse(text);
  13. }
  14. @Override
  15. public String print(Date date, Locale locale) {
  16. System.out.println("InitBinder addCustomFormatter Date to String ");
  17. return new SimpleDateFormat("yyyy-MM-dd").format(date);
  18. }
  19. });
  20. }
  • 请求
  1. // http://localhost:8080/dateType?date=2020-01-01
  2. @GetMapping("/dateType")
  3. public String dateType(Date date) {
  4. return "dateType date" + date;
  5. }

1.3.5、全局配置 Formatter

对于 json 参数(@RequestBody 修饰的参数)无效

  1. @Configuration
  2. public class WebConfig implements WebMvcConfigurer {
  3. /**
  4. * 注册 Converters 和 Formatters
  5. *
  6. * @param registry
  7. */
  8. @Override
  9. public void addFormatters(FormatterRegistry registry) {
  10. //参数传出格式化
  11. registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
  12. }
  13. }

1.3.6、@JsonFormat 单独配置字段格式化

只对 @RequestBody 修饰的参数有效

  • 定义实体
  1. @Data
  2. public class UserDate {
  3. @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM")
  4. private Date birthday;
  5. }
  • 请求
  1. /**
  2. * http://localhost:8080/dateType4
  3. * {
  4. * "birthday": "2020-08"
  5. * }
  6. */
  7. @PostMapping("/dateType4")
  8. @ResponseBody
  9. public UserDate dateType4(@RequestBody UserDate userDate) {
  10. return userDate;
  11. }

1.3.7、全局配置 JSON 参数日期格式化

注意: 全局配置后,依然可以使用 @JsonFormat 注解,用来接收特殊的日期参数格式。

  • 配置
  1. @Configuration
  2. public class WebConfig implements WebMvcConfigurer {
  3. @Override
  4. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  5. Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
  6. //指定时区
  7. .timeZone(TimeZone.getTimeZone("GMT+8:00"))
  8. //日期格式化
  9. .dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
  10. converters.add(0, new MappingJackson2HttpMessageConverter(builder.build()));
  11. }
  12. }
  • 实体
  1. @Data
  2. public class UserDate {
  3. @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM")
  4. private Date birthday;
  5. private Date date;
  6. }
  • 请求
  1. /**
  2. * http://localhost:8080/dateType4
  3. * {
  4. * "birthday": "2020-08",
  5. * "date": "2021-08-13"
  6. * }
  7. */
  8. @PostMapping("/dateType4")
  9. @ResponseBody
  10. public UserDate dateType4(@RequestBody UserDate userDate) {
  11. return userDate;
  12. }

1.4、 复杂类型

复杂类型包括数组和集合类型,像 List、Set、Map。以下以 List 为例

  • 使用逗号分割形式
  1. /**
  2. * 请求形式
  3. * http://localhost:8080/complexType2_1?list=1,2,3
  4. */
  5. @GetMapping("/complexType2_1")
  6. public String complexType2_1(@RequestParam("list") List<String> list) {
  7. return "complexType2_1 " + list;
  8. }
  • 相同参数明传递多次
  1. /**
  2. * 请求形式
  3. * http://localhost:8080/complexType2?list=1&list=2
  4. */
  5. @GetMapping("/complexType2")
  6. public String complexType2(@RequestParam("list") List<String> list) {
  7. return "complexType2 " + list;
  8. }
  • 使用 JSON 字符串传递
  1. /**
  2. * 请求形式
  3. * http://localhost:8080/complexType4
  4. * <p>
  5. * 请求体
  6. * [1,2,3]
  7. */
  8. @PostMapping("/complexType4")
  9. public String complexType4(@RequestBody List<String> list) {
  10. return "complexType4 " + list;
  11. }

1.5、 特殊类型

  • xml
  1. @Data
  2. @XmlRootElement(name ="user")
  3. public class User {
  4. Integer id;
  5. String name;
  6. }
  7. /**
  8. * http://localhost:8080/xmlType
  9. <?xml version="1.0" encoding="utf-8"?>
  10. <user>
  11. <id>1</id>
  12. <name>Java</name>
  13. </user>
  14. */
  15. @PostMapping(path = "/xmlType", consumes = "application/xml;charset=UTF-8")
  16. public String xmlType(@RequestBody User user) {
  17. return "xmlType " + user;
  18. }
  • json
  1. /**
  2. * 请求
  3. * http://localhost:8080/jsonType
  4. * 请求体
  5. {
  6. "id": 1,
  7. "name": "Java"
  8. }
  9. *
  10. * @RequestBody 不支持GET请求
  11. */
  12. @PostMapping(value = "/jsonType", consumes = "application/json")
  13. public String jsonType(@RequestBody User user) {
  14. return "jsonType " + user;
  15. }

二、了解底层实现

2.1、SpringMVC 方法参数绑定

2.1.1、认识 HandlerMethodArgumentResolver 接口
  1. public interface HandlerMethodArgumentResolver {
  2. //该解析器是否支持parameter参数的解析
  3. boolean supportsParameter(MethodParameter parameter);
  4. //从给定请求(webRequest)解析为参数值并填充到指定对象中
  5. @Nullable
  6. Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  7. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
  8. }

2.1.2、内置的 HandlerMethodArgumentResolver
  1. //在初始化RequestMappingHandlerAdapter 时会默认加载参数解析器
  2. // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet
  3. private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
  4. List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
  5. // Annotation-based argument resolution
  6. //处理 @RequestParam 注解标识的参数
  7. resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
  8. //处理@RequestParam 注解标识的Map参数且不能指定参数名称
  9. resolvers.add(new RequestParamMapMethodArgumentResolver());
  10. //处理@PathVariable 注解标识路径参数 如/pathVariable/{a}
  11. resolvers.add(new PathVariableMethodArgumentResolver());
  12. //处理@PathVariable 注解标识的Map参数且不能指定参数名称
  13. resolvers.add(new PathVariableMapMethodArgumentResolver());
  14. //处理@MatrixVariable注解标识的参数
  15. resolvers.add(new MatrixVariableMethodArgumentResolver());
  16. resolvers.add(new MatrixVariableMapMethodArgumentResolver());
  17. resolvers.add(new ServletModelAttributeMethodProcessor(false));
  18. //处理@RequestBody 注解
  19. resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
  20. resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
  21. //处理请求头
  22. resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
  23. resolvers.add(new RequestHeaderMapMethodArgumentResolver());
  24. //处理Cookie 值
  25. resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
  26. resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
  27. resolvers.add(new SessionAttributeMethodArgumentResolver());
  28. resolvers.add(new RequestAttributeMethodArgumentResolver());
  29. // Type-based argument resolution
  30. resolvers.add(new ServletRequestMethodArgumentResolver());
  31. resolvers.add(new ServletResponseMethodArgumentResolver());
  32. resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
  33. resolvers.add(new RedirectAttributesMethodArgumentResolver());
  34. resolvers.add(new ModelMethodProcessor());
  35. resolvers.add(new MapMethodProcessor());
  36. resolvers.add(new ErrorsMethodArgumentResolver());
  37. resolvers.add(new SessionStatusMethodArgumentResolver());
  38. resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
  39. // 添加自定义的解析器
  40. if (getCustomArgumentResolvers() != null) {
  41. resolvers.addAll(getCustomArgumentResolvers());
  42. }
  43. // Catch-all
  44. resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
  45. resolvers.add(new ServletModelAttributeMethodProcessor(true));
  46. return resolvers;
  47. }

2.1.2、执行过程
  • 初始化解析器到 RequestMappingHandlerAdapter 中
  1. // org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter
  2. @Bean
  3. public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
  4. @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
  5. @Qualifier("mvcConversionService") FormattingConversionService conversionService,
  6. @Qualifier("mvcValidator") Validator validator) {
  7. RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
  8. adapter.setContentNegotiationManager(contentNegotiationManager);
  9. adapter.setMessageConverters(getMessageConverters());
  10. adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
  11. //可以实现org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口
  12. //设置自定义的参数解析器
  13. adapter.setCustomArgumentResolvers(getArgumentResolvers());
  14. adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
  15. if (jackson2Present) {
  16. adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
  17. adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
  18. }
  19. AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
  20. configureAsyncSupport(configurer);
  21. if (configurer.getTaskExecutor() != null) {
  22. adapter.setTaskExecutor(configurer.getTaskExecutor());
  23. }
  24. if (configurer.getTimeout() != null) {
  25. adapter.setAsyncRequestTimeout(configurer.getTimeout());
  26. }
  27. adapter.setCallableInterceptors(configurer.getCallableInterceptors());
  28. adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
  29. return adapter;
  30. }
  31. // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet
  32. @Override
  33. public void afterPropertiesSet() {
  34. // Do this first, it may add ResponseBody advice beans
  35. initControllerAdviceCache();
  36. if (this.argumentResolvers == null) {
  37. //获取默认解析器 和 自定义解析器
  38. List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
  39. this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
  40. }
  41. if (this.initBinderArgumentResolvers == null) {
  42. List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
  43. this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
  44. }
  45. if (this.returnValueHandlers == null) {
  46. List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
  47. this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
  48. }
  49. }
  • 寻找合适的解析器
  1. //1. org.springframework.web.servlet.DispatcherServlet#doDispatch
  2. //2. org.springframework.web.servlet.HandlerAdapter#handle
  3. //3. org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
  4. //4. org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
  5. protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  6. Object... providedArgs) throws Exception {
  7. //获取方法参数
  8. MethodParameter[] parameters = getMethodParameters();
  9. if (ObjectUtils.isEmpty(parameters)) {
  10. return EMPTY_ARGS;
  11. }
  12. Object[] args = new Object[parameters.length];
  13. for (int i = 0; i < parameters.length; i++) {
  14. MethodParameter parameter = parameters[i];
  15. parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
  16. args[i] = findProvidedArgument(parameter, providedArgs);
  17. if (args[i] != null) {
  18. continue;
  19. }
  20. //判断是否支持解析该参数
  21. if (!this.resolvers.supportsParameter(parameter)) {
  22. throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
  23. }
  24. try {
  25. //HandlerMethodArgumentResolverComposite 组合模式
  26. //使用具体HandlerMethodArgumentResolver 解析参数
  27. args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  28. }
  29. catch (Exception ex) {
  30. // Leave stack trace for later, exception may actually be resolved and handled...
  31. if (logger.isDebugEnabled()) {
  32. String exMsg = ex.getMessage();
  33. if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
  34. logger.debug(formatArgumentError(parameter, exMsg));
  35. }
  36. }
  37. throw ex;
  38. }
  39. }
  40. return args;
  41. }
  • 解析参数
  1. // @RequestParam 注解的参数
  2. // org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
  3. //不同解析器实现不一样
  4. @Override
  5. @Nullable
  6. public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  7. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  8. //根据参数定义创建一个NamedValueInfo对象
  9. NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
  10. //如果参数是使用Optional包裹,则获取内嵌的参数对象
  11. MethodParameter nestedParameter = parameter.nestedIfOptional();
  12. // 处理参数名称
  13. Object resolvedName = resolveStringValue(namedValueInfo.name);
  14. if (resolvedName == null) {
  15. throw new IllegalArgumentException(
  16. "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
  17. }
  18. //解析请求参数值
  19. Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
  20. if (arg == null) {
  21. if (namedValueInfo.defaultValue != null) {
  22. arg = resolveStringValue(namedValueInfo.defaultValue);
  23. }
  24. else if (namedValueInfo.required && !nestedParameter.isOptional()) {
  25. handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
  26. }
  27. arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
  28. }
  29. else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
  30. arg = resolveStringValue(namedValueInfo.defaultValue);
  31. }
  32. if (binderFactory != null) {
  33. //创建WebDataBinder
  34. WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
  35. try {
  36. //转换请求参数为对应方法形参
  37. arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
  38. }
  39. catch (ConversionNotSupportedException ex) {
  40. throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
  41. namedValueInfo.name, parameter, ex.getCause());
  42. }
  43. catch (TypeMismatchException ex) {
  44. throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
  45. namedValueInfo.name, parameter, ex.getCause());
  46. }
  47. }
  48. //处理路径参数
  49. handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
  50. return arg;
  51. }

2.2、WebDataBinder 原理

2.2.1、初始化 WebDataBinder 方式
  • @Controller 在每个控制器中定义(或者提取到 BaseController )
  1. public class BaseController {
  2. // @InitBinder 注解的方法,返回值需要声明为void
  3. @InitBinder
  4. public void initBinderUser(WebDataBinder binder) {
  5. System.out.println("BaseController WebDataBinder 执行" );
  6. }
  7. }
  8. @RestController
  9. public class DemoDataBindingController extends BaseController {
  10. }
  1. @ControllerAdvice
  2. public class ControllerAdviceConfig {
  3. @InitBinder
  4. public void initBinderUser(WebDataBinder binder) {
  5. System.out.println("ControllerAdvice WebDataBinder 执行" );
  6. }
  7. }
  • 自定义 WebBindingInitializer
  1. //默认实现 ConfigurableWebBindingInitializer
  2. public interface WebBindingInitializer {
  3. // org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder 创建时调用
  4. // 比@InitBinder 注解的方法先执行
  5. void initBinder(WebDataBinder binder);
  6. @Deprecated
  7. default void initBinder(WebDataBinder binder, WebRequest request) {
  8. initBinder(binder);
  9. }
  10. }
  1. @Configuration
  2. public class CustomConfigurableWebBindingInitializer extends ConfigurableWebBindingInitializer {
  3. @Override
  4. public void initBinder(WebDataBinder binder) {
  5. super.initBinder(binder);
  6. System.out.println("CustomConfigurableWebBindingInitializer initBinder");
  7. }
  8. }
  9. //发起请求时,控制台输出
  10. //CustomConfigurableWebBindingInitializer initBinder
  11. //ControllerAdvice WebDataBinder 执行

2.2.2、WebDataBinder 有什么作用?
  • 用于绑定请求参数(Form 表单参数,query 参数)到模型对象中
  • 用于转换 字符串参数(请求参数、路径参数、header 属性、Cookie) 为 Controller 方法形参的对应类型
  • 格式化对象为指定字符串格式

2.2.3、WebDataBinder 执行过程
  • 定义初始化 WebDataBinder 方式(#2.2.1)
  • 创建 DataBinderFactory
  1. //1. org.springframework.web.servlet.DispatcherServlet#doDispatch
  2. //2. org.springframework.web.servlet.HandlerAdapter#handle
  3. //3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
  4. private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
  5. Class<?> handlerType = handlerMethod.getBeanType();
  6. Set<Method> methods = this.initBinderCache.get(handlerType);
  7. if (methods == null) {
  8. // 查找@Controller中 @InitBinder 注解的方法
  9. methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
  10. this.initBinderCache.put(handlerType, methods);
  11. }
  12. List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
  13. // Global methods first
  14. // initBinderAdviceCache 在 RequestMappingHandlerAdapter#afterPropertiesSet 里初始化
  15. // 1. 先加载 在@ControllerAdvice类定义的 @InitBinder 注解的方法
  16. for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
  17. if (entry.getKey().isApplicableToBeanType(handlerType)) {
  18. Object bean = entry.getKey().resolveBean();
  19. for (Method method : entry.getValue()) {
  20. initBinderMethods.add(createInitBinderMethod(bean, method));
  21. }
  22. }
  23. }
  24. //2. 再加载@Controller中 @InitBinder 注解的方法
  25. for (Method method : methods) {
  26. Object bean = handlerMethod.getBean();
  27. initBinderMethods.add(createInitBinderMethod(bean, method));
  28. }
  29. return createDataBinderFactory(initBinderMethods);
  30. }
  • 执行 initBinder 方法
  1. // org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
  2. // org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder
  3. @Override
  4. @SuppressWarnings("deprecation")
  5. public final WebDataBinder createBinder(
  6. NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
  7. WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
  8. if (this.initializer != null) {
  9. //执行 WebBindingInitializer 定义的initBinder方法
  10. this.initializer.initBinder(dataBinder, webRequest);
  11. }
  12. //执行 @InitBinder 注解的方法
  13. initBinder(dataBinder, webRequest);
  14. return dataBinder;
  15. }

到此,对 SpringMVC 的参数绑定讲解完成了。

项目地址

参考