当前位置: 首页 > 后端技术 > Java

SpringBoot自定义控制层参数分析

时间:2023-04-01 23:55:16 Java

1.背景在Spring的Controller中,我们可以通过@RequestParam或@RequestBody将request中的参数映射到控制层的具体参数,那么这是如何实现的呢?如果我当前控制层的某个参数的值来自于Redis,我该如何实现呢?2.参数是如何解析的从上图中我们的参数最终会通过HandlerMethodArgumentResolver来解析,所以知道了这些之后我们就可以实现自己的参数解析了。3、需求如果我们控制层方法的参数中有@Redis注解,那么这个参数的值应该是从redis中获取的,而不是从请求参数中获取的。从上图中我们可以看出参数@Redis(key="redisKey")StringredisValue需要从Redis中获取。4.实现这里我们并不会真正从Redis中获取值,只是模拟从Redis中获取值。1.写一个Redis注解@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)public@interfaceRedis{/***Keyinredis*/Stringkey();}在控制层的方法上,这里注解的方法参数都是从Redis中获取的,都是经过我们自己定义的参数解析器。2.编写参数分析类包com.huan.study.argument.resolver;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.core.MethodParameter;importorg.springframework.web.bind.support.WebDataBinderFactory;导入org.springframework.web.context.request.NativeWebRequest;导入org.springframework.web.method.support.HandlerMethodArgumentResolver;导入org.springframework.web.method.support.ModelAndViewContainer;导入javax.servquelet.http.HttpServletResolver;importjava.util.Random;/***从redis中获取值并放入参数中**/publicclassRedisMethodArgumentResolverimplementsHandlerMethodArgumentResolver{privatestaticfinalLoggerlog=LoggerFactory.getLogger(RedisMethodArgumentResolver.class);/****/@OverridepublicbooleansupportsParameter(MethodParameterparameter){returnparameter.hasParameterAnnotation(Redis.class);}/***解析参数*/@OverridepublicObjectresolveArgument(MethodParameterparameter,ModelAndViewContainermavContainer,NativeWebRequestwebRequest,WebDataBinderFactorybinderFactory)throwsException{//获取http请求HttpServletRequestrequest=webRequest.getNativeRequest(HttpServletRequest.class);log.info({"}当前请求路径"request.getRequestURI());//获取这个注解Redisredis=parameter.getParameterAnnotation(Redis.class);//获取redis中的keyStringredisKey=redis.key();//模拟从redis取值StringredisValue="从redis获取的值:"+newRandom().nextInt(100);log.info("从redis获取的值:[{}]",redisValue);//返回值returnredisValue;}}1.使用supportsParameter方法来确定我们应该处理哪些参数。在这里,我们处理带有@Redis注释的参数。2.通过resolveArgument方法获取参数的具体值。比如从Redis中获取,代码实际上并没有从Redis中获取,而是模拟从Redis中获取。3.在Spring的上下文中配置这里我们最好先把自己的参数解析器放上去,否则可能会出问题。下面提供了两种方法。第一种方式不能满足我们的需求。我们使用第二种方法实现1.Implement@ConfigurationpublicclassWebMvcConfigimplementsWebMvcConfigurerthroughWebMvcConfigurer{/***这个地方的加载顺序默认在*/HandlerMethodArgumentResolver之后@OverridepublicvoidaddArgumentResolvers(Listresolvers){resolvers.add(newRedisMethodArgumentResolver());}}从上图可以看出,我们自己的参数解析器不是第一个,所以可能会达到我们想要的效果,但达不到,这里不考虑这种方法。2.通过BeanPostProcessor实现BeanPostProcessor可以在一个Bean完全初始化后进行一些操作。这里我们就这样把自己的参数解析器放在首位。@ComponentstaticclassCustomHandlerMethodArgumentResolverConfigimplementsBeanPostProcessor{@OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{if(beaninstanceofRequestMappingHandlerAdapter){finalRequestMappingHandlerAdapter适配器=(RequestMappingHandlerAdapter)bean;finalListargumentResolvers=Optional.ofNullable(adapter.getArgumentResolvers()).orElseGet(ArrayList::new);finalArrayListhandlerMethodArgumentResolvers=newArrayList<>(argumentResolvers);//将我们自己的解析器放在第一位handlerMethodArgumentResolvers.add(0,newRedisMethodArgumentResolver());adapter.setArgumentResolvers(Collections.unmodifiableList(handlerMethodArgumentResolvers));返回适配器;}返回豆;}}从上图可以看出,首先是我们自己的参数解析器,这样才能达到我们想要的效果。这里使用了这个方法4.写一个简单的控制层/***@authorhuan.fu2021/12/7-3:36PM*/@RestControllerpublicclassRedisArgumentController{privatestaticfinalLoggerlog=LoggerFactory.getLogger(RedisArgumentController。班级);@GetMapping("redisArgumentResolver")publicvoidredisArgumentResolver(@RequestParam("hello")Stringhello,@Redis(key="redisKey")StringredisValue){log.info("控制层获取的参数值:hello:[{}],redisValue:[{}]",你好,redisValue);}}控制层比较简单,对外提供了一个简单的apihttp://localhost:8080/redisArgumentResolver?hello=123。该api有两个参数hello和redisValue,其中hello参数的值是从请求参数中获取的,redisValue的值是从我们自己定义的参数解析器中获取的。5、测试curlhttp://localhost:8080/redisArgumentResolver?hello=123由上图可以看出,我们自己定义的参数解析器起作用了。六、完整代码完整代码