扩展SpringMVC的请求参数解析能力
考虑这样一个功能,我们有一个地址字段,格式为:省/市/县
。前端传值为字符串。我们如何用一个地址类型(Address
)来接收这个参数呢?地址类型的字段如下:
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Address {
private String province;
private String city;
private String street;
}
我们假设格式是固定的,每个地址一定有省市县,不存在缺失一项的情况。当然现实中肯定是有这种情况的,此处只是为了说明如何对SpringMVC的参数解析能力进行扩展,因此不考虑特殊情况的处理。
第一种方法:扩展Converter
首先我们定义一个StringToAddressConverter
,用于将字符串转换为Address
对象。
public class StringToAddressConverter implements Converter<String, Address> {
@Override
public Address convert(@NonNull String source) {
String[] parts = source.split("/");
if (parts.length == 3) {
return new Address(parts[0], parts[1], parts[2]);
}
return new Address();
}
}
然后修改SpringMVC配置,将其添加到ConversionService
中。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToAddressConverter());
}
}
控制层在使用时,直接在要转为地址类型的参数前增加@RequestParam
注解即可。
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/address")
public String address(@RequestParam Address address) {
System.out.println(address);
return "OK";
}
}
然后启动服务,发送请求
GET http://localhost:8080/test/address?address=河南省/郑州市/高新区
控制台输出
Address(province=河南省, city=郑州市, street=高新区)
可以看到已经可以正常解析了。这种解析方式是通过RequestParamMethodArgumentResolver
来解析的。
如果不加@RequestParam
也是可以正常解析的。 这时候则是通过ServletModelAttributeMethodProcessor
来进行解析的。
第二种方法:扩展HandlerMethodArgumentResolver
我们首先定义一个参数注解 @AddressParam
,用于标注某个请求参数是一个地址对象。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AddressParam {
}
然后定义一个AddressHandlerMethodArgumentResolver
用于解析@AddressParam
标注的参数。
public class AddressHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(AddressParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String parameterName = parameter.getParameterName();
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
String value = servletRequest.getParameter(parameterName);
if (value == null) {
return new Address();
}
String[] parts = value.split("/");
if (parts.length == 3) {
return new Address(parts[0], parts[1], parts[2]);
}
return new Address();
}
}
最后配置一下
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new AddressHandlerMethodArgumentResolver());
}
}
让我们在控制层实际使用一下看看。
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/address")
public String address(@AddressParam Address address) {
System.out.println(address);
return "OK";
}
}
然后启动服务,发送请求
GET http://localhost:8080/test/address?address=河南省/郑州市/高新区
控制台输出
Address(province=河南省, city=郑州市, street=高新区)
总结
以上便是常用的两种扩展SpringMVC请求参数解析能力的方法。简单总结一下
- 第一种:扩展
Converter
,增加类型解析能力。这个类型解析的能力可以扩散到所有的HandlerMethodArgumentResolver
,因为Converter
最终会添加到ConversionService
中,这样就相当于为所有HandlerMethodArgumentResolver
添加了一种解析类型的支持。 - 第二种:扩展
HandlerMethodArgumentResolver
,增加一个新的解析器,其目的其实是为了添加新的解析方式。
所以综合来说,上面提到的实例更适合用扩展Converter
的方式来处理。