SpringBoot中自定义参数解析器,实现如@RequestParam的功能

文章目录

  • SpringBoot中自定义参数解析器,实现如@RequestParam的功能
    • 1. 自定义参数解析器
      • 1-1 参数解析器要实现的接口 (内置接口,这里作解释用)
      • 1-2 自定义参数解析器示例
        • 1-2-1 示例需求描述
        • 1-2-2 自定义一个 @CurrentUserName 注解
        • 1-2-3 自定义参数解析器 CurrentUserNameHandlerMethodArgumentResolver
        • 1-2-4 新建配置类WebConfig,将自定义的参数解析器配置到 HandlerAdapter
    • 2. 内置@RequestParam注解 的 代码参考
      • 2-1 日常使用示例
      • 2-2 RequestParamMapMethodArgumentResolver解析器代码

SpringBoot中自定义参数解析器,实现如@RequestParam的功能

1. 自定义参数解析器

1-1 参数解析器要实现的接口 (内置接口,这里作解释用)

// 自定义参数解析器需要实现 HandlerMethodArgumentResolver 接口,我们先看看该接口:
public interface HandlerMethodArgumentResolver {
	 boolean supportsParameter(MethodParameter parameter);
	 @Nullable
	 Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
	   NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

// 这个接口中就两个方法:
// 		supportsParameter:	该方法表示是否启用这个参数解析器,返回 true 表示启用,返回 false 表示不启用。
// 		resolveArgument:	这是具体的解析过程,就是从 request 中取出参数的过程,方法的返回值就对应了接口中参数的值。

// 	自定义参数解析器只需要实现该接口即可

1-2 自定义参数解析器示例

1-2-1 示例需求描述

需求描述:
	假设有这样一个需求,在方法的参数上添加了 @CurrentUserName 注解(名称自己定义的),
	那么该参数的值就是当前登录的用户名,像下面这样:
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(@CurrentUserName String name) {
        return "hello "+name;
    }
}

1-2-2 自定义一个 @CurrentUserName 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CurrentUserName {
}

1-2-3 自定义参数解析器 CurrentUserNameHandlerMethodArgumentResolver

// supportsParameter:如果参数类型是 String,并且参数上有 @CurrentUserName注解,则使用该参数解析器。
// resolveArgument:该方法的返回值就是参数的具体值,当前登录用户名从 SecurityContextHolder 中获取即可

public class CurrentUserNameHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(String.class)&&parameter.hasParameterAnnotation(CurrentUserName.class);
    }
 
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return user.getUsername();
    }
}

1-2-4 新建配置类WebConfig,将自定义的参数解析器配置到 HandlerAdapter

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

2. 内置@RequestParam注解 的 代码参考

2-1 日常使用示例

@RestController
public class HelloController {
    @PostMapping("/hello")
    // 这里参数上使用@RequestParam注解,会自动将接口传入的Map映射到参数map中
    public void hello(@RequestParam MultiValueMap map) throws IOException {
        //省略...
    }
}

2-2 RequestParamMapMethodArgumentResolver解析器代码

// supportsParameter:参数类型是 Map,并且使用了 @RequestParam 注解,
// 					并且 @RequestParam 注解中没有配置 name 属性,就可以使用该参数解析器。
// resolveArgument:具体解析分为两种情况:MultiValueMap 和其他 Map,
// 					前者中又分三种情况:MultipartFile、Part 或者其他普通请求,
// 					前两者可以处理文件上传,第三个就是普通参数。如果是普通 Map,
// 					则直接获取到原始请求参数放到一个 Map 集合中返回即可
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
 
 @Override
 public boolean supportsParameter(MethodParameter parameter) {
  RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
  return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
    !StringUtils.hasText(requestParam.name()));
 }
 
 @Override
 public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
   NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
 
  ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
 
  if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
   // MultiValueMap
   Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
   if (valueType == MultipartFile.class) {
    MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
    return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));
   }
   else if (valueType == Part.class) {
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
     Collection<Part> parts = servletRequest.getParts();
     LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size());
     for (Part part : parts) {
      result.add(part.getName(), part);
     }
     return result;
    }
    return new LinkedMultiValueMap<>(0);
   }
   else {
    Map<String, String[]> parameterMap = webRequest.getParameterMap();
    MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
    parameterMap.forEach((key, values) -> {
     for (String value : values) {
      result.add(key, value);
     }
    });
    return result;
   }
  }
 
  else {
   // Regular Map
   Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve();
   if (valueType == MultipartFile.class) {
    MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
    return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));
   }
   else if (valueType == Part.class) {
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
     Collection<Part> parts = servletRequest.getParts();
     LinkedHashMap<String, Part> result = CollectionUtils.newLinkedHashMap(parts.size());
     for (Part part : parts) {
      if (!result.containsKey(part.getName())) {
       result.put(part.getName(), part);
      }
     }
     return result;
    }
    return new LinkedHashMap<>(0);
   }
   else {
    Map<String, String[]> parameterMap = webRequest.getParameterMap();
    Map<String, String> result = CollectionUtils.newLinkedHashMap(parameterMap.size());
    parameterMap.forEach((key, values) -> {
     if (values.length > 0) {
      result.put(key, values[0]);
     }
    });
    return result;
   }
  }
 }
 
}

你可能感兴趣的:(#,SpringBoot,spring,boot,java,spring)