一、背景
在 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 个参数 hello
和redisValue
,其中 hello
参数的值是从申请参数中获取,redisValue
的值是从咱们本人定义的参数
解析器中获取。
五、测试
curl http://localhost:8080/redisArgumentResolver?hello=123
由上图可知咱们本人定义的参数解析器工作了。
六、残缺代码
残缺代码