SpringBoot自定义参数解析器

164 阅读6分钟

SpringBoot自定义参数解析

1.为什么要自定义参数解析

1.原因

如不自定义参数解析

假如说需要获取Token,如果直接处理,需要在每个接口上都要添加

HttpServletRequest request

或者

@RequestHeader("token") String token

这样显得非常麻烦

    @RequestMapping(value = "/user/findAll", method = RequestMethod.GET)
    public RestBean findAllUser(HttpServletRequest request) throws Exception {
        //token在请求头中,需要获取请求头中的token
        //获取请求头中的Token,并判断
        String token = request.getHeader("token");
        //判断Token是否为空
        if (StringUtils.hasText(token)) {
            //解析Token获取用户id
            Claims claims = JwtUtil.parseJWT(token);
            //获取用户id
            String id = claims.getSubject();
            //打印用户id
            System.out.println(id);
        }
        List<User> userList = userService.findAllUser();
        //判断是否查询到用户
        if (userList.isEmpty())
            return new RestBean(500, "查询失败");
        else
            return new RestBean(200, "查询成功", userList);
    }


	   //查询所有用户
    @RequestMapping(value = "/user/findAll1", method = RequestMethod.GET)
    public RestBean findAllUser1(@RequestHeader("token") String token) throws Exception {

        //判断Token是否为空
        if (StringUtils.hasText(token)) {
            //解析Token获取用户id
            Claims claims = JwtUtil.parseJWT(token);
            //获取用户id
            String id = claims.getSubject();
            //打印用户id
            System.out.println(id);
        }
        List<User> userList = userService.findAllUser();
        //判断是否查询到用户
        if (userList.isEmpty())
            return new RestBean(500, "查询失败");
        else
            return new RestBean(200, "查询成功", userList);
    }

如果自定义参数解析,在获取一些参数的时候会更加的方便,也有助于提高代码的复用性

假如很多接口需要这些参数,就显得非常麻烦

2.自定义参数解析

1.自定义解析

如果我们想要实现像获取请求体数据那样,在方法的参数上添加一个@RequestBody注解就可以获取到对应的数据的话

public RestBean findUserById(@RequestBody("id") int id)

就需要使用HandlerMethodArgumentResolver来实现自定义参数的解析

HandlerMethodArgumentResolver是Spring MVC框架中的一个接口,它用于解析处理方法参数。

2.自定义的注解

package jiasen.resolver;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


//@ElementType.PARAMETER:用于描述参数
//@ElementType.METHOD:用于描述方法
//Retention:注解的生命周期
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUserID {
}

3.实现自定义解析

需要实现HandlerMethodArgumentResolver

需要将该方法注册到Spring容器中,所以需要添加@Component注解注入Spring容器

package jiasen.resolver;

import io.jsonwebtoken.Claims;
import jiasen.util.JwtUtil;
import org.apache.ibatis.annotations.Param;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
//自定义参数解析器
public class UserIdArgumentResolver implements HandlerMethodArgumentResolver {

    //判断方法参数是否能使用当前的参数解析器进行解析
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        //判断方法参数是否有某些注解
        //如果方法参数上有@CurrentUserID注解,返回值为true,则会调用resolveArgument方法进行参数解析,如果为false,则不会调用
        return parameter.hasParameterAnnotation(CurrentUserID.class);
    }
    //进行参数解析的方法,可以在方法中获取对应的数据,然后将数据作为返回值返回.
    //方法的返回值就会赋值给赋值给对应的方法参数


    /*
    * 1.MethodParameter parameter:获取当前方法的参数信息
    * 可以通过该参数获取方法的参数名称,参数类型,参数上的注解等信息
    * 2.ModelAndViewContainer mavContainer:当前方法所在的类的方法的参数信息
    * 用于方法参数的绑定
    * 3.NativeWebRequest webRequest:用于获取请求相关的信息
    * 可以获取请求参数,请求头,session,cookie等用于解析参数的信息
    * 4.WebDataBinderFactory binderFactory:用于创建WebDataBinder对象,用于数据绑定和验证
    * 如果需要进行数据绑定和验证,可以通过该参数获取WebDataBinder对象
    * */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
       //获取请求值中的Token
        String token = webRequest.getHeader("Token");

        if (StringUtils.hasText(token)){
            //解析Token,获取用户ID
            Claims claims = JwtUtil.parseJWT(token);
            //将用户ID转换为字符串
            String userid = claims.getSubject();
            //返回用户ID
            return userid;
        }
        return null;
    }
}

supportsParameter:该方法用于判断解析器是否支持给定的方法参数,开发人员可以在这个方法中定义自己的逻辑,根据参数类型/注解...条件来判断是否可以解析该参数

resolveArgument该方法用于解析给定的方法参数并返回解析结果,如果上面的方法为true,将会使用该方法进行解析

resolveArgument方法中的参数

  1. MethodParameter parameter: 表示要解析的方法参数。通过该参数可以获取参数的类型、注解、泛型信息等。
  2. ModelAndViewContainer mavContainer: 用于处理返回视图的容器。在参数解析期间,可以使用该容器来设置或获取与请求处理相关的模型和视图信息
  3. NativeWebRequest webRequest: 表示当前的原生Web请求对象。通过该对象可以获取请求的各种属性和参数,例如请求头、请求参数、路径变量,request,Session等
  4. WebDataBinderFactory binderFactory: 用于创建和获取数据绑定器的工厂对象。数据绑定器用于将请求数据绑定到目标对象上。通过该工厂对象,可以获取数据绑定器以进行进一步的数据绑定操作。

通过parameter参数获取方法参数的类型信息,根据类型信息进行不同的处理逻辑。

使用webRequest参数获取请求中的数据,例如请求参数、请求头等。根据这些数据进行相应的处理和转换。

使用mavContainer参数设置或获取与请求处理相关的模型和视图信息。可以将解析结果放入模型中,以供后续的视图渲染使用。

使用binderFactory参数创建数据绑定器,并根据需要进行数据绑定操作,将请求数据绑定到目标对象上。

4.配置

和其他例如拦截器,跨域配置一样,需要实现WebMvcConfigurer,重写其中的方法

package jiasen.config;

import jiasen.resolver.UserIdArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

//自定义参数解析器配置
@Configuration
public class ArgumentResolverConfig implements WebMvcConfigurer {
    //将参数解析器注入到spring容器中
    @Autowired
    private UserIdArgumentResolver  userIdArgumentResolver;

    //添加参数解析器
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userIdArgumentResolver);
    }
}

5.测试

没有添加自定义注解,需要使用request请求,从请求头中获取token进行解析,或者使用@RequestHeader注解进行解析

	//第一种
	@RequestMapping(value = "/user/findAll", method = RequestMethod.GET)
    public RestBean findAllUser(HttpServletRequest request) throws Exception {
        //token在请求头中,需要获取请求头中的token
        //获取请求头中的Token,并判断
        String token = request.getHeader("token");
        //判断Token是否为空
        if (StringUtils.hasText(token)) {
            //解析Token获取用户id
            Claims claims = JwtUtil.parseJWT(token);
            //获取用户id
            String id = claims.getSubject();
            //打印用户id
            System.out.println(id);
        }
        List<User> userList = userService.findAllUser();
        //判断是否查询到用户
        if (userList.isEmpty())
            return new RestBean(500, "查询失败");
        else
            return new RestBean(200, "查询成功", userList);
    }
	
	//第二种
    @RequestMapping(value = "/user/findAll", method = RequestMethod.GET)
    public RestBean findAllUser2(@RequestHeader("token") String token) throws Exception {

        //判断Token是否为空
        if (StringUtils.hasText(token)) {
            //解析Token获取用户id
            Claims claims = JwtUtil.parseJWT(token);
            //获取用户id
            String id = claims.getSubject();
            //打印用户id
            System.out.println(id);
        }
        List<User> userList = userService.findAllUser();
        //判断是否查询到用户
        if (userList.isEmpty())
            return new RestBean(500, "查询失败");
        else
            return new RestBean(200, "查询成功", userList);
    }


image-20230813000423017.png

使用自定义注解后,可以直接获取,不需要使用request请求中解析,并且可以将解析Token逻辑写在实现了HandlerMethodArgumentResolver方法的类上

    //查询所有用户
    @RequestMapping(value = "/user/findAll1", method = RequestMethod.GET)
    public RestBean findAllUser1(@CurrentUserID String userID) throws Exception {

        System.out.println(userID);

        List<User> userList = userService.findAllUser();
        //判断是否查询到用户
        if (userList.isEmpty())
            return new RestBean(500, "查询失败");
        else
            return new RestBean(200, "查询成功", userList);
    }

image-20230812235507076.png