【工作篇】再次熟悉SpringMVC 參數(shù)綁定

前言

主要現(xiàn)在項(xiàng)目中使用的參數(shù)綁定五花八門的,搞得很頭大,例如有些用字符串接收日期,用字符串接受數(shù)組等等,完全沒有利用好 SpringMVC 的優(yōu)勢,這里自己也總結(jié)一下,免得到時(shí)又要百度谷歌查找。

以下實(shí)踐的 Spring 版本是:5.2.7.RELEASE

一、SpringMVC 中不同類型的數(shù)據(jù)綁定

1.1、基礎(chǔ)數(shù)據(jù)類型

  • 默認(rèn)參數(shù)名
  // http://localhost:8080/baseType3?a=123
  @GetMapping("/baseType")
  @ResponseBody
  public String baseType(int a) {
      return "baseType " + a;
  }
  • 使用@RequestParam 自定義請求參數(shù)名稱
  //  http://localhost:8080/baseType3?b=123
  @GetMapping("/baseType3")
  @ResponseBody
  public String baseType3(@RequestParam(value = "b", required = true) Integer a) {
      return "baseType3 " + a;
  }
  • 多個(gè)參數(shù)
  //  http://localhost:8080/baseType4?age=10&name=Java
  @GetMapping("/baseType4")
  public String baseType3(@RequestParam Integer age, String name) {
      return "baseType4  age:" + age + "  name="+name;
  }

1.2、 對象類型

超過三個(gè)參數(shù)及以上,則推薦使用對象來接收傳遞的參數(shù)

  • 定義簡單對象接收參數(shù)
  @Data //這里使用了 lombok 插件
  public class User {
      Integer id;
      String name;
  }

  // http://localhost:8080/objectType?id=1&name=Java
  @GetMapping("/objectType")
  public String objectType(User user) {
      return "objectType " + user;
  }
  • 內(nèi)嵌對象接收參數(shù)
  @Data
  public class Order {
      Integer id;
      User user;
  }

  // http://localhost:8080/objectType2?id=1&user.name=Java&user.id=2
  @GetMapping("/objectType2")
  public String objectType2(Order order) {
      return "objectType2 " + order;
  }
  • 使用 DataBinder 解決不同對象,參數(shù)名相同覆蓋問題

    • 定義對象
  @Data
  public class Friend {
      Integer id;
      String name;  //與User 對象name 名稱沖突
  }
  
  @Data
  public class User {
      Integer id;
      String name;
  }
  • InitBinder 配置

    在 Controller 中定義,只對當(dāng)前 Controller 有效,也可以在 @ControllerAdvice 類中全局定義

/**
     * 初始化綁定參數(shù)user 標(biāo)識(shí)前綴
     *
     * @param binder
     */
@InitBinder("user")
public void initBinderUser(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("user.");
}

/**
     * 初始化綁定參數(shù)friend 標(biāo)識(shí)前綴
     *
     * @param binder
     */
@InitBinder("friend")
public void initBinderFriend(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("friend.");
}
  • 編寫請求
//http://localhost:8080/objectType3?name=Java  name會(huì)同時(shí)填充到User 和Friend對象上
//http://localhost:8080/objectType3?user.name=Java&friend.name=Python 分別填充數(shù)據(jù)到各自的對象中去
@GetMapping("/objectType3")
public String objectType3(User user, Friend friend) {
    return "objectType3  user" + user + "  friend " + friend;
}

1.3、 日期類型

日期類型的參數(shù)傳遞方式比較多,正式項(xiàng)目中建議統(tǒng)一規(guī)定日期類型的參數(shù)綁定的格式

1.3.1、使用時(shí)間戳傳遞(不是參數(shù)綁定方式)
// http://localhost:8080/dateType6?date=1628752881
@GetMapping("/dateType6")
public String dateType5(Long date) {
    return "dateType6  date" + new Date(date);
}
1.3.2、使用字符串接收(不是參數(shù)綁定方式)
// http://localhost:8080/dateType7?date=2021-08-12
@GetMapping("/dateType7")
public String dateType7(String date) throws ParseException {
    return "dateType7  date" + new SimpleDateFormat("yyyy-MM-dd").parse(date);
}
1.3.3、使用 SpringMVC 默認(rèn)提供的 @DateTimeFormat (對于 json 參數(shù)無效)
// http://localhost:8080/dateType2?date1=2020-01-01
@GetMapping("/dateType2")
public String dateType2(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1) {
    return "dateType2  date " + date1;
}
1.3.4、使用 @InitBinder 注冊轉(zhuǎn)換器
  • 添加轉(zhuǎn)換器
    /**
     * 注冊日期轉(zhuǎn)換 date
     *
     * @param binder
     */
    @InitBinder
    public void initBinderDate(WebDataBinder binder) {
        binder.addCustomFormatter(new Formatter<Date>() {
            @Override
            public Date parse(String text, Locale locale) throws ParseException {
                System.out.println("InitBinder addCustomFormatter String to Date  ");
                return new SimpleDateFormat("yyyy-MM-dd").parse(text);
            }

            @Override
            public String print(Date date, Locale locale) {
                System.out.println("InitBinder addCustomFormatter  Date to String  ");
                return new SimpleDateFormat("yyyy-MM-dd").format(date);
            }
        });
    }
  • 請求
// http://localhost:8080/dateType?date=2020-01-01
@GetMapping("/dateType")
public String dateType(Date date) {
    return "dateType  date" + date;
}
1.3.5、全局配置 Formatter

對于 json 參數(shù)(@RequestBody 修飾的參數(shù))無效

@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 注冊 Converters 和 Formatters
     *
     * @param registry
     */
        @Override
    public void addFormatters(FormatterRegistry registry) {
        //參數(shù)傳出格式化
        registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
    }
}
1.3.6、@JsonFormat 單獨(dú)配置字段格式化

只對 @RequestBody 修飾的參數(shù)有效

  • 定義實(shí)體
@Data
public class UserDate {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM")
    private Date birthday;
}
  • 請求
/**
  * http://localhost:8080/dateType4
  * {
  *     "birthday": "2020-08"
  * }
*/
@PostMapping("/dateType4")
@ResponseBody
public UserDate dateType4(@RequestBody UserDate userDate) {
    return userDate;
}
1.3.7、全局配置 JSON 參數(shù)日期格式化

注意: 全局配置后,依然可以使用 @JsonFormat 注解,用來接收特殊的日期參數(shù)格式。

  • 配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                //指定時(shí)區(qū)
                .timeZone(TimeZone.getTimeZone("GMT+8:00"))
                //日期格式化
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        converters.add(0, new MappingJackson2HttpMessageConverter(builder.build()));
    }
}
  • 實(shí)體
@Data
public class UserDate {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM")
    private Date birthday;

    private Date date;
}
  • 請求
/**
  * http://localhost:8080/dateType4
  * {
  *     "birthday": "2020-08",
  *     "date": "2021-08-13"
  * }
*/
@PostMapping("/dateType4")
@ResponseBody
public UserDate dateType4(@RequestBody UserDate userDate) {
    return userDate;
}

1.4、 復(fù)雜類型

復(fù)雜類型包括數(shù)組和集合類型,像 List、Set、Map。以下以 List 為例

  • 使用逗號分割形式
    /**
     * 請求形式
     * http://localhost:8080/complexType2_1?list=1,2,3
     */
    @GetMapping("/complexType2_1")
    public String complexType2_1(@RequestParam("list") List<String> list) {
        return "complexType2_1 " + list;
    }

  • 相同參數(shù)明傳遞多次
    /**
     * 請求形式
     * http://localhost:8080/complexType2?list=1&list=2
     */
    @GetMapping("/complexType2")
    public String complexType2(@RequestParam("list") List<String> list) {
        return "complexType2 " + list;
    }
  • 使用 JSON 字符串傳遞
/**
     * 請求形式
     * http://localhost:8080/complexType4
     * <p>
     * 請求體
     * [1,2,3]
     */
@PostMapping("/complexType4")
public String complexType4(@RequestBody List<String> list) {
    return "complexType4 " + list;
}

1.5、 特殊類型

  • xml
@Data
@XmlRootElement(name ="user")
public class User {
    Integer id;
    String name;
}

/**
     * http://localhost:8080/xmlType

 <?xml version="1.0" encoding="utf-8"?>
 <user>
 <id>1</id>
 <name>Java</name>
 </user>
     */
@PostMapping(path = "/xmlType", consumes = "application/xml;charset=UTF-8")
public String xmlType(@RequestBody User user) {
    return "xmlType " + user;
}
  • json

/**
     * 請求
     * http://localhost:8080/jsonType
     * 請求體
{
    "id": 1,
    "name": "Java"
}
     *
     * @RequestBody 不支持GET請求
     */
@PostMapping(value = "/jsonType", consumes = "application/json")
public String jsonType(@RequestBody User user) {
    return "jsonType " + user;
}

二、了解底層實(shí)現(xiàn)

2.1、SpringMVC 方法參數(shù)綁定

2.1.1、認(rèn)識(shí) HandlerMethodArgumentResolver 接口
public interface HandlerMethodArgumentResolver {

    //該解析器是否支持parameter參數(shù)的解析
    boolean supportsParameter(MethodParameter parameter);

    //從給定請求(webRequest)解析為參數(shù)值并填充到指定對象中
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
2.1.2、內(nèi)置的 HandlerMethodArgumentResolver
//在初始化RequestMappingHandlerAdapter 時(shí)會(huì)默認(rèn)加載參數(shù)解析器
// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
   List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

   // Annotation-based argument resolution

   //處理 @RequestParam 注解標(biāo)識(shí)的參數(shù)
   resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
   //處理@RequestParam 注解標(biāo)識(shí)的Map參數(shù)且不能指定參數(shù)名稱
   resolvers.add(new RequestParamMapMethodArgumentResolver());
   //處理@PathVariable 注解標(biāo)識(shí)路徑參數(shù) 如/pathVariable/{a}
   resolvers.add(new PathVariableMethodArgumentResolver());
   //處理@PathVariable 注解標(biāo)識(shí)的Map參數(shù)且不能指定參數(shù)名稱
   resolvers.add(new PathVariableMapMethodArgumentResolver());

   //處理@MatrixVariable注解標(biāo)識(shí)的參數(shù)
   resolvers.add(new MatrixVariableMethodArgumentResolver());
   resolvers.add(new MatrixVariableMapMethodArgumentResolver());


   resolvers.add(new ServletModelAttributeMethodProcessor(false));

   //處理@RequestBody 注解
   resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
   //處理請求頭
   resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new RequestHeaderMapMethodArgumentResolver());
   //處理Cookie 值
   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());

   // 添加自定義的解析器
   if (getCustomArgumentResolvers() != null) {
      resolvers.addAll(getCustomArgumentResolvers());
   }

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

   return resolvers;
}
2.1.2、執(zhí)行過程
  • 初始化解析器到 RequestMappingHandlerAdapter 中
// org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
    @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
    @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    @Qualifier("mvcValidator") Validator validator) {

    RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    adapter.setContentNegotiationManager(contentNegotiationManager);
    adapter.setMessageConverters(getMessageConverters());
    adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));


    //可以實(shí)現(xiàn)org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口
    //設(shè)置自定義的參數(shù)解析器
    adapter.setCustomArgumentResolvers(getArgumentResolvers());

    adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    if (jackson2Present) {
        adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
        adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }
    AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
    configureAsyncSupport(configurer);
    if (configurer.getTaskExecutor() != null) {
        adapter.setTaskExecutor(configurer.getTaskExecutor());
    }
    if (configurer.getTimeout() != null) {
        adapter.setAsyncRequestTimeout(configurer.getTimeout());
    }
    adapter.setCallableInterceptors(configurer.getCallableInterceptors());
    adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
    return adapter;
}

// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet
@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        //獲取默認(rèn)解析器 和 自定義解析器
        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);
    }
}
  • 尋找合適的解析器
//1. org.springframework.web.servlet.DispatcherServlet#doDispatch
//2. org.springframework.web.servlet.HandlerAdapter#handle
//3. org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
//4. org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
    //獲取方法參數(shù)
    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;
        }
        //判斷是否支持解析該參數(shù)
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            //HandlerMethodArgumentResolverComposite 組合模式
            //使用具體HandlerMethodArgumentResolver 解析參數(shù)
            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ù)
// @RequestParam 注解的參數(shù)
// org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
//不同解析器實(shí)現(xiàn)不一樣
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    //根據(jù)參數(shù)定義創(chuàng)建一個(gè)NamedValueInfo對象
    NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    //如果參數(shù)是使用Optional包裹,則獲取內(nèi)嵌的參數(shù)對象
    MethodParameter nestedParameter = parameter.nestedIfOptional();
    // 處理參數(shù)名稱
    Object resolvedName = resolveStringValue(namedValueInfo.name);
    if (resolvedName == null) {
        throw new IllegalArgumentException(
            "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
    }
    //解析請求參數(shù)值
    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    if (arg == null) {
        if (namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }
        else if (namedValueInfo.required && !nestedParameter.isOptional()) {
            handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
        arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    }
    else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
        arg = resolveStringValue(namedValueInfo.defaultValue);
    }

    if (binderFactory != null) {
        //創(chuàng)建WebDataBinder
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            //轉(zhuǎn)換請求參數(shù)為對應(yīng)方法形參
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
        catch (ConversionNotSupportedException ex) {
            throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                                                                    namedValueInfo.name, parameter, ex.getCause());
        }
        catch (TypeMismatchException ex) {
            throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                                                          namedValueInfo.name, parameter, ex.getCause());
        }
    }
    //處理路徑參數(shù)
    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

    return arg;
}

2.2、WebDataBinder 原理

2.2.1、初始化 WebDataBinder 方式
  • @Controller 在每個(gè)控制器中定義(或者提取到 BaseController )
public class BaseController {
    // @InitBinder 注解的方法,返回值需要聲明為void
    @InitBinder
    public void initBinderUser(WebDataBinder binder) {
        System.out.println("BaseController  WebDataBinder 執(zhí)行" );
    }
}

@RestController
public class DemoDataBindingController extends BaseController {
}
  • @ControllerAdvice 類 中定義,每個(gè)請求都會(huì)執(zhí)行,適合全局配置
@ControllerAdvice
public class ControllerAdviceConfig {

    @InitBinder
    public void initBinderUser(WebDataBinder binder) {
        System.out.println("ControllerAdvice  WebDataBinder 執(zhí)行" );
    }
}
  • 自定義 WebBindingInitializer
//默認(rèn)實(shí)現(xiàn) ConfigurableWebBindingInitializer
public interface WebBindingInitializer {
    // org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder 創(chuàng)建時(shí)調(diào)用
    // 比@InitBinder 注解的方法先執(zhí)行
    void initBinder(WebDataBinder binder);

    @Deprecated
    default void initBinder(WebDataBinder binder, WebRequest request) {
        initBinder(binder);
    }
}
@Configuration
public class CustomConfigurableWebBindingInitializer extends ConfigurableWebBindingInitializer {

    @Override
    public void initBinder(WebDataBinder binder) {
        super.initBinder(binder);

        System.out.println("CustomConfigurableWebBindingInitializer  initBinder");
    }
}

//發(fā)起請求時(shí),控制臺(tái)輸出
//CustomConfigurableWebBindingInitializer  initBinder
//ControllerAdvice  WebDataBinder 執(zhí)行
2.2.2、WebDataBinder 有什么作用?
  • 用于綁定請求參數(shù)(Form 表單參數(shù),query 參數(shù))到模型對象中
  • 用于轉(zhuǎn)換 字符串參數(shù)(請求參數(shù)、路徑參數(shù)、header 屬性、Cookie) 為 Controller 方法形參的對應(yīng)類型
  • 格式化對象為指定字符串格式
2.2.3、WebDataBinder 執(zhí)行過程
  • 定義初始化 WebDataBinder 方式(#2.2.1)
  • 創(chuàng)建 DataBinderFactory
//1. org.springframework.web.servlet.DispatcherServlet#doDispatch
//2. org.springframework.web.servlet.HandlerAdapter#handle
//3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
    Class<?> handlerType = handlerMethod.getBeanType();
    Set<Method> methods = this.initBinderCache.get(handlerType);
    if (methods == null) {
        // 查找@Controller中 @InitBinder 注解的方法
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        this.initBinderCache.put(handlerType, methods);
    }
    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
    // Global methods first
    // initBinderAdviceCache 在 RequestMappingHandlerAdapter#afterPropertiesSet 里初始化
    // 1. 先加載 在@ControllerAdvice類定義的 @InitBinder 注解的方法
    for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
        if (entry.getKey().isApplicableToBeanType(handlerType)) {
            Object bean = entry.getKey().resolveBean();
            for (Method method : entry.getValue()) {
                initBinderMethods.add(createInitBinderMethod(bean, method));
            }
        }
    }
    //2. 再加載@Controller中 @InitBinder 注解的方法
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    return createDataBinderFactory(initBinderMethods);
}
  • 執(zhí)行 initBinder 方法
// org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
// org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder
@Override
@SuppressWarnings("deprecation")
public final WebDataBinder createBinder(
    NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {

    WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
    if (this.initializer != null) {
        //執(zhí)行 WebBindingInitializer 定義的initBinder方法
        this.initializer.initBinder(dataBinder, webRequest);
    }
    //執(zhí)行 @InitBinder 注解的方法
    initBinder(dataBinder, webRequest);
    return dataBinder;
}

到此,對 SpringMVC 的參數(shù)綁定講解完成了。

項(xiàng)目地址

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容