共计 2262 个字符,预计需要花费 6 分钟才能阅读完成。
springmvc4.1+ 可能反对 optional 参数,但不反对对象内 field 的 optional。
举个例子
// 对于这种间接在办法上定义的 optional 的根本类型或者援用类型
// 比方参数里没有 id,或者没有 obj,那么 spring mvc 会设置一个空的 Optional 对象
// 咱们能够通过 Optional 对象进行空判断
@GetMapping("/get")
public void test1(Optional<String> id, Optional<Object> obj) {id.ifPresent(i->{});
}
// 然而对于援用类型里 field 的 Optional 的话,spring mvc 不反对
// 定义一个对象,外面属性用 Optional 类型
@Data
public class TestVo {
Optional<Integer> id;
Optional<String> name;
}
// 如果这时候参数传了 id,那么 testVo.getId().get() 能够拿到值
// 然而如果没有传 name,这时的 testVo 的 name 属性为 null 而不是 Optional 的 empty 类型
@GetMapping("/get")
public Object test(TestVo testVo) {System.err.println(testVo.getId().get());
System.err.println(testVo.getName().isPresent()); // 报空指针异样
return testVo;
}
咱们来看看 springmvc 是怎么解决的?
对于办法参数,spring mvc 是如何赋值的呢?
mvc 有个 RequestMappingHandlerAdapter,名称能够看出申请映射解决适配,就是它匹配咱们拜访的 url 门路和 controller 的 requestMapping 门路的,其中蕴含了一个 HandlerMethodReturnValueHandler,处理结果赋值,以及 HandlerMethodArgumentResolver(咱们讲这个),解决办法参数解析,就是由它进行参数赋值的,它有很多实现类,比方 PathVariableMapMethodArgumentResolver 就是咱们在参数上定义一个 @PathVariable 时进行赋值的实现,RequestParamMapMethodArgumentResolver 就是咱们在参数上定义一个 @RequestParam 时进行赋值的实现。
那么平时默认的没有注解的援用类型,是通过哪个解析器呢?通过断点可知是通过 ServletModelAttributeMethodProcessor 来实现的。外面有 WebDataBinder(题外话,WebDataBinder 里有个 ConversionService,这是 spring 提供的一个转换接口,底层应用 BeanWrapper 进行 bean 的操作),理论实现类是 ExtendedServletRequestDataBinder(该类就是进行数据绑定的),bind 办法里就是获取 request 的参数列表,因为咱们只传了 id,没有传 name,所以这里返回的 MutablePropertyValues 时外面就只有 id(问题就出在这里),并设置 id 的值。
对于援用类型会应用到 PropertyDescriptor(一系列的,属于 java bean 的标准)类设置列属性的值,TypeDescriptor 记录属性类型,而后会依据属性名和参数类型获取 PropertyEditor(PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName)),如果 editor 为空的话,就会优先应用后面提到的 ConversionService(ConversionService conversionService = this.propertyEditorRegistry.getConversionService())实现参数类型转换。断点能够看到对于 id 字段应用的是 ObjectToOptionalConverter 类型,其实对于 web 参数申请类型,源参数类型都为 string 类型,也就是(TypeDescriptor 的类型为 string)。
发现如果对象外面还有个援用类型的属性字段且是 Optional 类型,则 springmvc 会抛异样 Auto-growing not allowed with private constructor: private java.util.Optional(),这是因为,在 springmvc 进行赋值时,首先会调用无参结构初始对象,这时候因为 java 的泛型擦除,只能获取到 Optional 类型,而不能获取到具体的类型,Optional 的结构是公有的就报错了。。所以 springmvc 是不反对对象嵌套应用 Optional 的。
那咱们须要对象里的根本类型字段设置 Optional 怎么弄呢?
原本想着在执行完 springmvc 的赋值前或者赋值后,设置对象里为 null 的 Optional 字段,后果看了一下,代码里写的很死,不能扩大 webDataBinder,除非重写 RequestMappingHandlerAdapter 的 createDataBinderFactory 办法,返回一个自定义的 webdatabinder,这个就比拟麻烦不贴代码了。。