跳到主要内容

扩展SpringMVC的请求参数解析能力

考虑这样一个功能,我们有一个地址字段,格式为:省/市/县。前端传值为字符串。我们如何用一个地址类型(Address)来接收这个参数呢?地址类型的字段如下:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Address {
private String province;
private String city;
private String street;
}
java

我们假设格式是固定的,每个地址一定有省市县,不存在缺失一项的情况。当然现实中肯定是有这种情况的,此处只是为了说明如何对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();
}
}

java

然后修改SpringMVC配置,将其添加到ConversionService中。

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToAddressConverter());
}

}
java

控制层在使用时,直接在要转为地址类型的参数前增加@RequestParam注解即可。

@RestController
@RequestMapping("/test")
public class TestController {

@GetMapping("/address")
public String address(@RequestParam Address address) {
System.out.println(address);
return "OK";
}
}
java

然后启动服务,发送请求

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 {

}
java

然后定义一个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();
}
}
java

最后配置一下

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new AddressHandlerMethodArgumentResolver());
}
}
java

让我们在控制层实际使用一下看看。

@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/address")
public String address(@AddressParam Address address) {
System.out.println(address);
return "OK";
}
}
java

然后启动服务,发送请求

GET http://localhost:8080/test/address?address=河南省/郑州市/高新区

控制台输出

Address(province=河南省, city=郑州市, street=高新区)

总结

以上便是常用的两种扩展SpringMVC请求参数解析能力的方法。简单总结一下

  • 第一种:扩展Converter,增加类型解析能力。这个类型解析的能力可以扩散到所有的HandlerMethodArgumentResolver,因为Converter最终会添加到ConversionService中,这样就相当于为所有HandlerMethodArgumentResolver添加了一种解析类型的支持。
  • 第二种:扩展HandlerMethodArgumentResolver,增加一个新的解析器,其目的其实是为了添加新的解析方式。

所以综合来说,上面提到的实例更适合用扩展Converter的方式来处理。