一、背景

在Spring的Controller中,我们通过@RequestParam@RequestBody就可以将请求中的参数映射到控制层具体的参数中,那么这个是怎么实现的呢?如果我现在控制层中的某个参数的值是从Redis中来,那么应该如何实现呢?

二、参数是如何解析的

从上图中可以我们的参数最终会通过HandlerMethodArgumentResolver来解析,那么知道了这个后,我们就可以实现自己的参数解析了。

三、需求

如果我们控制层方法的参数中存在@Redis标注,那么此参数的值应该从redis中获取,不用从请求参数中获取。

从上图中可知@Redis(key = "redisKey") String redisValue这个参数就需要从Redis中获取。

四、实现

此处我们不会真的从Redis中获取值,模拟从Redis中获取值即可。

1、编写一个Redis注解

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Redis { /**
* redis中的Key
*/
String key(); }

在控制层的方法上,被此处注解标注的方法参数,都从Redis中获取,都走我们自己定义的参数解析器。

2、编写参数解析类

package com.huan.study.argument.resolver;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
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; import javax.servlet.http.HttpServletRequest;
import java.util.Random; /**
* 从redis中获取值放入到参数中
*
*/
public class RedisMethodArgumentResolver implements HandlerMethodArgumentResolver { private static final Logger log = LoggerFactory.getLogger(RedisMethodArgumentResolver.class); /**
* 处理参数上存在@Redis注解的
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Redis.class);
} /**
* 解析参数
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
// 获取http request
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
log.info("当前请求的路径:[{}]", request.getRequestURI());
// 获取到这个注解
Redis redis = parameter.getParameterAnnotation(Redis.class);
// 获取在redis中的key
String redisKey = redis.key();
// 模拟从redis中获取值
String redisValue = "从redis中获取的值:" + new Random().nextInt(100);
log.info("从redis中获取到的值为:[{}]", redisValue);
// 返回值
return redisValue;
}
}

1、通过supportsParameter方法判断我们应该处理哪些参数,此处处理的是参数上存在@Redis注解的。

2、通过resolveArgument方法,获取到参数的具体的值。比如从Redis中获取,代码中没有真的从Redis中获取,只是模拟从Redis中获取。

3、配置到Spring的上下文中

此处我们最好将我们自己的参数解析器放置在第一位,否则可能会有问题。下方提供了2种方式,第一种方式无法达到我们的需求、我们采用第二种方式来实现

1、通过WebMvcConfigurer实现

@Configuration
public class WebMvcConfig implements WebMvcConfigurer { /**
* 这个地方加载的顺序是在默认的HandlerMethodArgumentResolver之后的
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RedisMethodArgumentResolver());
}
}

从上图可知,我们自己的参数解析器不是在第一位,这样可能达不到我们想要的效果,此处不考虑这种方式。

2、通过BeanPostProcessor来实现

BeanPostProcessor可以在一个Bean完全初始化之后来执行一些操作,此处我们通过这种方式,将我们自己的参数解析器放置在第一位。

@Component
static class CustomHandlerMethodArgumentResolverConfig implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
final RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
final List<HandlerMethodArgumentResolver> argumentResolvers = Optional.ofNullable(adapter.getArgumentResolvers())
.orElseGet(ArrayList::new);
final ArrayList<HandlerMethodArgumentResolver> handlerMethodArgumentResolvers = new ArrayList<>(argumentResolvers);
// 将我们自己的参数解析器放置到第一位
handlerMethodArgumentResolvers.add(0, new RedisMethodArgumentResolver());
adapter.setArgumentResolvers(Collections.unmodifiableList(handlerMethodArgumentResolvers));
return adapter;
}
return bean;
}
}

从上图可知,我们自己的参数解析器在第一位,这样就达到我们想要的效果,此处使用这种方式。

4、编写一个简单的控制层

/**
* @author huan.fu 2021/12/7 - 下午3:36
*/
@RestController
public class RedisArgumentController { private static final Logger log = LoggerFactory.getLogger(RedisArgumentController.class); @GetMapping("redisArgumentResolver")
public void redisArgumentResolver(@RequestParam("hello") String hello,
@Redis(key = "redisKey") String redisValue) {
log.info("控制层获取到的参数值: hello:[{}],redisValue:[{}]", hello, redisValue);
}
}

该控制层比较简单,对外提供来一个简单的apihttp://localhost:8080/redisArgumentResolver?hello=123。该api存在2个参数helloredisValue,其中hello参数的值是从请求参数中获取,redisValue的值是从我们自己定义的参数

解析器中获取。

五、测试

curl http://localhost:8080/redisArgumentResolver?hello=123

由上图可知我们自己定义的参数解析器工作了。

六、完整代码

完整代码

SpringBoot自定义控制层参数解析的更多相关文章

  1. [Go] 命令行参数解析包(flag 包)使用详解

    Go 的 flag 包可以解析命令行的参数. 一.命令行语法 命令行语法主要有以下几种形式: cmd -flag       // 只支持bool类型 cmd -flag=xxx cmd -flag ...

  2. springboot之json传参(后台控制层如何接收和解析参数)

    一般web端都是用form标签的形式进行表单提交到后台,后台控制层再用相应的实体对象去接收前端传来的json参数. 但是有时候前端界面很复杂,要传入后端的参数是各种标签里面的value值,这些值又是来 ...

  3. SpringBoot让你的Bean动起来(自定义参数解析HandlerMethodArgumentResolver)

    SpringBoot让你的Bean动起来(自定义参数解析HandlerMethodArgumentResolver) 简介 我们 Controller 用到的一些 Bean 需要通过一定的方式去获取的 ...

  4. SpringBoot系列教程web篇之如何自定义参数解析器

    title: 190831-SpringBoot系列教程web篇之如何自定义参数解析器 banner: /spring-blog/imgs/190831/logo.jpg tags: 请求参数 cat ...

  5. SpringBoot自定义参数解析器

    一.背景 平常经常用 @RequestParam注解来获取参数,然后想到我能不能写个自己注解获取请求的ip地址呢?就像这样 @IP String ip 二.分析 于是开始分析 @RequestPara ...

  6. Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验,使用消息资源文件对消息国际化

    导包和配置 导入 JSR 303 的包.hibernate valid 的包 <dependency> <groupId>org.hibernate.validator< ...

  7. SpringMVC自动封装List对象 —— 自定义参数解析器

    前台传递的参数为集合对象时,后台Controller希望用一个List集合接收数据. 原生SpringMVC是不支持,Controller参数定义为List类型时,接收参数会报如下错误: org.sp ...

  8. 自定义HandlerMethodArgumentResolver参数解析器和源码分析

    在初学springmvc框架时,我就一直有一个疑问,为什么controller方法上竟然可以放这么多的参数,而且都能得到想要的对象,比如HttpServletRequest或HttpServletRe ...

  9. 自定义springmvc参数解析器

    实现spring HandlerMethodArgumentResolver接口 通过使用@JsonArg自定义注解来解析json数据(通过fastjson的jsonPath),支持多个参数(@Req ...

随机推荐

  1. 实验8:数据平面可编程实践——P4

    一.实验目的 掌握V1Model框架下P4_16的程序结构和基本语法 能够运用 P4 进行简单数据平面编程 二.实验报告 在修改basic_tunnel.p4的内容之后输入make run 验证创建结 ...

  2. [bzoj1677]求和

    dp,用f[i]表示i划分的方案,直接枚举最后一个数是错误的,因为会导致c重复计数,然后正解十分神奇--当i为奇数,那么分解中一定有1,因此f[i]=f[i-1]当i为偶数若有1,同样转移到f[i-1 ...

  3. 未能加载文件或程序集“Microsoft.CodeDom.Providers.DotNetCompilerPlatform

    "/"应用程序中的服务器错误. 未能加载文件或程序集"Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Versio ...

  4. 调试:'Object reference note set to an instance of an object.'

    今天调试代码遇到一个奇怪的问题,每次调试到 var files = new List<string>()这一行代码,总是报错:System.NullReferenceException: ...

  5. 从ApacheTomcat架构谈面试到源码编译环境v10.0.12

    概述 开启博客分享已近三个月,感谢所有花时间精力和小编一路学习和成长的伙伴们,有你们的支持,我们继续再接再厉 **本人博客网站 **IT小神 www.itxiaoshen.com 定义 Tomcat官 ...

  6. 进击的 Ansible(二):如何快速搞定生产环境 Ansible 项目布局?

    Tips:与前文 <进击的 Ansible(一):Ansible 快速入门> 一样,本文使用的 Ansible 版本 2.5.4,项目演示环境 MacOS.由于 Ansible 项目开发活 ...

  7. CF1463E Plan of Lectures

    考虑我们两种操作: 我们把第一种操作在\(x\to y\)连一条权为-1的边. 第二种操作\(x\to y\)连-1,\(y\to x\)连1的边. 当无法操作则是环里有负环. 否则我们把第二种操作涉 ...

  8. [CF707 Div2, A ~ D]

    (相信进这个博客的人,都已经看过题目了,不再赘述) 这把打小号打到了\(484\),\(rating + 636\) \(A\) 考虑进行模拟就行了,说白了这是一个英语阅读题 // code by D ...

  9. urllib的基本使用介绍

    1. urllib中urlopen的基本使用介绍 1 ### urllib中urlopen的基本使用介绍 2 3 ## urlopen的基本用法(GET请求) 4 import urllib.requ ...

  10. 完全用Deepin Linux娱乐、工作、学习(1)

    截至今天我已经用全Deepin Desktop Linux环境娱乐.工作.学习了100多天.当你看到这个桌面的时候,会不会觉得它是MacOS?错了,它是Deepin Desktop Linux,而且它 ...