@InitBinder注解会用吗?该如何使用?
创始人
2025-07-14 00:11:43
0

环境: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() {
    @Override
    public Integer convert(String source) {
      return Integer.parseInt(source) + 1 ;
    }
  }) ;
}

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

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...