前一篇博客使用TypeConvert实现类型String到LocalDate/LocalDateTime转换,使用TypeConvert对于创建简单的值类型并且将其注入到Controller对应的方法,是一个不错的选择。
但是,有时候我们注入的对象需要满足如下这些要求:
- 注入的对象包含多个字段,并且对应的字段值需要从多个源进行读取
- 注入的对象并不是一个表单对象
- 注入的对象不是从request body中传入
如果我们需要满足上述说的这些要求,就需要创建一个自定义的HandlerMethodArgumentResolver,那这篇博客就是为了描述我们如何做到这一点。
创建一个自定义的HandlerMethodArgumentResolver
新建一个FooBar类,包含两个final修饰的字段:bar 和 foo
private final String foo;
private final String bar;
public FooBar(String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
@Override
public String toString() {
return "FooBar{" +
"foo='" + foo + '\'' +
", bar='" + bar + '\'' +
'}';
}
通过如下步骤,创建自定义的HandlerMethodArgumentResolver,用于创建FooBar对象:
- 实现HandlerMethodArgumentResolver 接口
- 实现supportsParameter(MethodParameter methodParameter)方法,如果方法参数是FooBar类型返回true,其余返回false
- 实现resolveArgument() 方法,逻辑如下:
(1)从请求参数获bar的值
(2)从请求参数获foo的值
(3)如果请求参数中不存在bar的值,使用默认值
(4)如果请求参数中不存在foo的值,使用默认值
(5)创建一个新的FooBar对象并返回
具体代码如下:
public class CustomMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
if(parameter.getParameterType().equals(FooBar.class)){
return true;
}
return false;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String bar = webRequest.getParameter("bar");
String foo = webRequest.getParameter("foo");
if(null == bar){
bar = "defaultBar";
}
if(null == foo){
bar = "defaultFoo";
}
return new FooBar(foo, bar);
}
}
配置web应用上下文信息
在我们注入FooBar 对象到Controller方法之前,我们需要注册CustomMethodArgumentResolver 作为方法参数的解析器,下面我们将介绍如何在springboot中进行注册
- 新建CustomWebMvcExtend类,并实现WebMvcConfigurer接口
- 覆写addArgumentResolvers方法,并将CustomMethodArgumentResolver 添加进resolvers列表对象中
@Configuration
public class CustomWebMvcExtend implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CustomMethodArgumentResolver());
}
}
测试功能
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableWebMvc
@RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RequestMapping(value = "/foobar", method = RequestMethod.POST)
public String processFooBar(FooBar fooBar){
System.out.println(fooBar);
return "test is ok";
}
}
测试结果请求输出如下:
2022-02-07 23:08:07.633 INFO 19876 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-02-07 23:08:07.634 INFO 19876 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-02-07 23:08:07.634 INFO 19876 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
FooBar{foo='2021-12-13', bar='defaultBar'}
总结:
如果我们想要注入复杂类型的对象进入Controller方法,这些对象既不是表单对象也不是来自于request body中,这时候我们就需要创建一个自定义的HandlerMethodArgumentResolver 接口实现类
在自定义的HandlerMethodArgumentResolver 接口实现类生效之前,我们需要将其注册到上下文环境的resolvers列表内。