1. Background
In the Spring Controller, we can map the parameters in the request to the specific parameters of the control layer @RequestParam
or @RequestBody
If the value of a parameter in the control layer is from Redis
, how should I achieve it?
Second, how the parameters are parsed
From the above figure, our parameters will eventually be HandlerMethodArgumentResolver
, then after knowing this, we can implement our own parameter analysis.
3. Demand
@Redis
label in the parameters of our control layer method, then the value of this parameter should be obtained from redis, not from the request parameters.
From the figure above shows @Redis(key = "redisKey") String redisValue
this parameter is required from Redis
get in.
Fourth, realize
Here we will not really Redis
, just simulate getting the value from Redis.
1. Write a Redis annotation
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Redis {
/**
* redis中的Key
*/
String key();
}
In the method of the control layer, the method parameters marked by the annotations here are all obtained from Redis, and all use our own defined parameter parser.
2. Write parameter analysis class
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. Use the supportsParameter
method to determine which parameters we should deal with. What we deal with here is the existence of @Redis
annotations on the parameters.
2. Obtain the specific value of the parameter through the resolveArgument
For example, if you get it from Redis, the code doesn't really get it from Redis, it just simulates getting it from Redis.
3. Configure into the context of Spring
Here we'd better put our own parameter parser in the first place, otherwise there may be problems. Two ways are provided below, the first way cannot meet our needs, we use the second way to achieve
1. Realized through WebMvcConfigurer
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 这个地方加载的顺序是在默认的HandlerMethodArgumentResolver之后的
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RedisMethodArgumentResolver());
}
}
It can be seen from the above figure that our own parameter parser is not in the first place, so it may not achieve the effect we want. This method is not considered here.
2. Realized by BeanPostProcessor
BeanPostProcessor
can perform some operations after a Bean is fully initialized. Here we put our own parameter parser in the first place in this way.
@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;
}
}
From the above figure, we can see that our own parameter parser is in the first place, so that we can achieve the effect we want. This method is used here.
4. Write a simple control layer
/**
* @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);
}
}
The control layer is relatively simple, and a simple api http://localhost:8080/redisArgumentResolver?hello=123
provided to the outside. The api has two parameters hello
and redisValue
, among which hello
parameter is obtained from the request parameter, and redisValue
is the parameter defined by ourselves
Obtained from the parser.
Five, test
curl http://localhost:8080/redisArgumentResolver?hello=123
From the figure above, we can see that the parameter parser defined by ourselves is working.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。