1

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.

@Redis标注的参数

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.

Six, complete code

complete code


huan1993
207 声望31 粉丝

java工程师