@InitBinder注解会用吗?该如何使用?

开发 前端
@InitBinder 方法可以注册特定控制器的 java.beans.PropertyEditor 或 Spring Converter 和 Formatter 组件。此外,还可以使用 MVC 配置在全局共享的 FormattingConversionService 中注册转换器和格式器类型。

环境:SpringBoot2.7.16

1. 简介

@Controller 或 @ControllerAdvice 类可以使用 @InitBinder 注解标注方法来初始化 WebDataBinder 实例,WebDataBinder对象主要用来做数据绑定操作;具体使用了@InitBinder注解的方法可以做如下事情:

  • 将请求参数(即表单或查询数据)绑定到模型对象。
  • 将基于字符串的请求值(如请求参数、路径变量、头信息、cookie 等)转换为控制器方法参数的目标类型。
  • 在呈现 HTML 表单时,将模型对象值格式化为字符串值。

@InitBinder 方法可以注册特定控制器的 java.beans.PropertyEditor 或 Spring Converter 和 Formatter 组件。此外,还可以使用 MVC 配置在全局共享的 FormattingConversionService 中注册转换器和格式器类型。

2. 定义初始化DataBinder

被@InitBinder标注的方法的返回值必须是void。否则将抛出异常。

@InitBinder
public void initBinder(WebDataBinder binder) {
  // ...
}

源码分析:

以有如下简单的接口参数解析绑定进行分析

@GetMapping("/index")
public Object index(Integer id) {
  // 
}

当请求/index?id=666时Spring MVC会通过参数解析器RequestParamMethodArgumentResolver进行参数类型的转换及绑定(接收到的'666'是字符串将转换为Integer)。

public class RequestParamMethodArgumentResolver ...{}
// 调用父类的方法
public abstract class AbstractNamedValueMethodArgumentResolver {
  public final Object resolveArgument(...) {
    // ...
    if (binderFactory != null) {
      WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
    }
    // ...
  }
}
// 绑定工厂
public class DefaultDataBinderFactory {
  public final WebDataBinder createBinder(...) {
    WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
    if (this.initializer != null) {
      this.initializer.initBinder(dataBinder, webRequest);
    }
    // 初始化绑定(执行所有被@InitBinder注解的方法)
    initBinder(dataBinder, webRequest);
    return dataBinder;
  }
}
// 绑定工厂实现类
public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
  public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
    // 这里的每一个InvocableHandlerMethod都表示被@InitBinder标准的方法对象
    for (InvocableHandlerMethod binderMethod : this.binderMethods) {
      if (isBinderMethodApplicable(binderMethod, dataBinder)) {
        Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
        // 如果存在返回值将抛出异常
        if (returnValue != null) {
          throw new IllegalStateException(
              "@InitBinder methods must not return a value (should be void): " + binderMethod);
        }
      }
    }
  }
}

3. 实战案例

3.1 在Controller中定义

@InitBinder
public void initBinder(WebDataBinder binder) {
  binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
      // 为了看到效果,这里吧转换后的值进行+1操作
      setValue(Integer.parseInt(text) + 1) ; 
    }
  }) ;
}

定义Controller接口

@GetMapping("/index")
public String index(Integer id) {
  return "转换后id: " + id ;
}

输出结果

图片图片

3.2 在ControllerAdvice中定义

在@ControllerAdvice中定义的@InitBinder方法将在所有或部分Controller中生效。

@ControllerAdvice()
public class BinderControllerAdvice {


  @InitBinder
  public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Integer.class, new PropertyEditorSupport() {
      @Override
      public void setAsText(String text) throws IllegalArgumentException {
        System.out.println("原始值:" + text) ;
        setValue(Integer.parseInt(text) + 1) ; 
      }
    }) ;
  }
}

为什么说也可以是部分Controller呢?

在@ControllerAdvice注解的属性中我们可以通过配置包,类及注解等信息。

@ControllerAdvice(basePackages = {"com.pack.service"})
public class BinderControllerAdvice {
  // ...
}

如上配置后,该类中的所有@InitBinder只会针对com.pack.service包中的Controller生效。

图片图片

3.3 注册为全局共享

可以使用 MVC 配置在全局共享的 FormattingConversionService 中注册转换器和格式器类型。

默认情况下SpringBoot容器会注册一个默认的全局类型转换器,如下:

@Bean
@Override
public FormattingConversionService mvcConversionService() {
  Format format = this.mvcProperties.getFormat();
  WebConversionService conversionService = new WebConversionService(
      new DateTimeFormatters().dateFormat(format.getDate())
        .timeFormat(format.getTime())
        .dateTimeFormat(format.getDateTime()));
  addFormatters(conversionService);
  return conversionService;
}

也就是说我们可以吧类型转换直接注册到上面的FormattingConversionService 中。

@InitBinder
public void initBinder(WebDataBinder binder) {
  FormattingConversionService fcs = (FormattingConversionService) binder.getConversionService();
  fcs.addConverter(new Converter<String, Integer>() {
    @Override
    public Integer convert(String source) {
      return Integer.parseInt(source) + 1 ;
    }
  }) ;
}

这样注册以后,我们可以在任意的地方从容器中获取到ConversionService实例就可以使用到上面自定义的转换服务。

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2022-06-28 14:57:09

FormatterSpring

2023-06-30 08:26:24

Java注解Java程序元素

2020-03-06 10:25:10

注解Java代码

2021-03-24 08:25:08

@SentinelReSentinelSpring

2022-10-27 11:46:48

ConfiguratValuespring

2021-03-12 18:25:09

开发前端React

2021-03-29 09:37:17

SpringBoot常用注解Spring Boot

2021-09-06 10:42:18

Linux命令服务器

2021-08-11 10:00:51

缓存MyBatis管理

2021-03-11 08:00:00

存储数据SSD

2022-11-11 08:31:39

Java注解注解类

2019-07-25 12:46:32

Java高并发编程语言

2019-01-28 17:42:33

Python数据预处理数据标准化

2018-09-29 15:34:34

JavaList接口

2019-04-14 22:33:52

网络层协议VLAN虚拟局域网

2021-05-21 12:36:16

限流代码Java

2020-06-04 14:15:55

Java中BigDecimal函数

2024-03-06 08:15:03

@Autowired注入方式Spring

2023-04-28 12:37:59

Spring@Bean使用方式

2023-04-23 09:50:50

@BeanSpring
点赞
收藏

51CTO技术栈公众号