> 文章列表 > SpringBoot接口传递自定义参数,参数解析器

SpringBoot接口传递自定义参数,参数解析器

SpringBoot接口传递自定义参数,参数解析器

Hi I’m Shendi


SpringBoot接口传递自定义参数,参数解析器

简介

我的需求:编写了一个日志微服务,使用方式是 创建日志对象 - 日志流程 - 完成日志对象,这样的方式使用时就需要在每个接口都去创建和完成一下,多出了一点代码。

在 SpringBoot 中,我们接收接口的参数都是直接写在函数参数上,例如传递了一个name

@GetMapping("/test")
public String test(String name) {return name;
}

于是就想到能不能像上面这种方式将创建和完成封装起来,就开始寻找解决方案。

之前一直使用的是过滤器,但我的需求过滤器是没有办法实现的,过滤器可以给请求注入字符串,但不能注入对象

例如 User 类,想要的效果如下

@GetMapping("/test")
public String test(User user) {return user.toString();
}

后面学了下拦截器,发现拦截器也不行…

后面发现可以使用addArgumentResolvers来实现接口增加参数,自定义参数解析器

编写实现类

可以继承以下两个接口

  • WebArgumentResolver
  • HandlerMethodArgumentResolver

这两个接口都是用来处理控制器方法参数的接口。不同之处在于:

  1. HandlerMethodArgumentResolver是在Spring 3.1之后引入的,用于处理注解控制器方法参数。它是一个更加灵活、更加强大的解决方案,可以处理更多的操作,例如类型转换、注入依赖、权限验证等等。它可以用于处理@RequestParam、@PathVariable、@RequestBody、@RequestHeader等注解。
  2. WebArgumentResolver是在Spring 3.0和3.1版本中都有,但在3.1版本中已经被HandlerMethodArgumentResolver所取代。它主要用于处理旧版的控制器方法参数,例如Servlet API中的HttpServletRequest和HttpServletResponse对象。但是它缺乏HandlerMethodArgumentResolver的灵活性,不能处理更为复杂的场景。

对于新项目而言,应该优先使用HandlerMethodArgumentResolver,可以获得更加灵活、强大、可扩展的参数处理能力。

本文就使用 HandlerMethodArgumentResolver 来实现给所有接口注入参数

实现HandlerMethodArgumentResolver接口,需要实现两个函数

public class TestResolve implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter parameter) {// TODO Auto-generated method stubreturn false;}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {// TODO Auto-generated method stubreturn null;}}

supportsParameter方法用于判断参数是否符合特定的类型,符合则执行resolveArgument

而resolveArgument方法则用于将从请求中获取的参数值转换为特定的参数类型,从而将其作为参数传递给方法。(返回什么对象那么接口中使用的就是什么对象,所以需要先判断对象类型再做操作,类型不同则报错)

在配置类注册

编写一个类实现 WebMvcConfigurer,这个在使用拦截器时也是用这个配置

WebMvcConfigurer是一个Spring框架中的配置接口,用于配置Spring MVC的默认行为和定制化处理程序。它定义了多个方法,包括添加资源处理器、拦截器和视图控制器等。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import shendi.resolve.TestResolve;@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new TestResolve());}}

测试使用

给Resolver类改一下,输出类型

public class TestResolve implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter parameter) {System.out.println("supportsParameter:" + parameter.getParameterType());return true;}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {System.out.println(parameter.getParameterType());return null;}}

可以看到 supportsParameter 返回 true,那么就会执行 resolveArgument

随便写个接口,函数接收两个参数(类型需要是当前项目的类,不然无效或者报错)

例如测试接口如下

public static class User {public String account;public String pwd;public String name;
}@GetMapping("/")public String get(String test, User user) {System.out.println("接口执行");return "test";
}

函数有两个参数,一个String一个User,运行,请求接口,控制台输出如下

SpringBoot接口传递自定义参数,参数解析器

supportsParameter 返回值改为 false,测试结果如下

SpringBoot接口传递自定义参数,参数解析器

可以很明显的看到区别。

参数注入

根据上面的部分,已经实现了参数的注入了,只不过注入的是null

我们可以在 supportsParameter 判断是否是我们需要注入的类,是则返回 true,否则false

@Override
public boolean supportsParameter(MethodParameter parameter) {// 判断相等可以直接用equals或者==,不过isAssignableFrom是本地(native)函数,可以判断是否有继承关系return parameter.getParameterType().isAssignableFrom(User.class);
}

然后在 resolveArgument 创建实例返回

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {// 如果当前有多个类支持的话,就需要用if判断是哪个类,没有则可直接创建对象返回if (parameter.getParameterType().isAssignableFrom(User.class)) {User user = new User();user.account = "shendi";return user;}return null;
}

这样,可以在接口直接使用 user 对象了

@GetMapping("/")
public String get(String test, User user) {System.out.println("接口执行: " + user.account);return "test";
}

SpringBoot接口传递自定义参数,参数解析器

配合过滤器实现我的需求

在这里再说下我的需求

我的需求:编写了一个日志微服务,使用方式是 创建日志对象 - 日志流程 - 完成日志对象,这样的方式使用时就需要在每个接口都去创建和完成一下,多出了一点代码。

根据上面那部分,已经可以给接口注入参数了,因为我的需求是日志使用,在过滤器中也需要使用,所以配合过滤器

我的理解,过滤器 - 接口,接口中解析参数时才用参数解析器,于是直接将对象放入请求的attribute,然后在解析器拿到返回就可以了

代码如下

@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {OLog olog = null;try {String id = ReqUtil.getUserIdStr(req);if (id == null) {olog = new OLog("接口", uri);} else {olog = new OLog("接口", uri, Integer.parseInt(id));}req.setAttribute("olog", olog);chain.doFilter(req, resp);olog.finish();} catch (Exception e) {e.printStackTrace();if (olog != null) olog.finish(OLog.RESULT_NOOK);}
}

public class ArgumentResolve implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.getParameterType().isAssignableFrom(OLog.class);}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {// 如果当前有多个类支持的话,就需要判断是哪个类if (parameter.getParameterType().isAssignableFrom(OLog.class)) {return webRequest.getAttribute("olog", NativeWebRequest.SCOPE_REQUEST);}return null;}}

END