当前位置:硬件测评 > Spring源码解读(第十版)——定制、请求参数的控制和返回值处理

Spring源码解读(第十版)——定制、请求参数的控制和返回值处理

  • 发布:2023-10-01 20:06

又到了定制(安装13)的时间了,你准备好了吗?

通过上一篇文章《spring源码解读(第九弹)》,我想大家已经掌握了请求参数封装和返回值处理的核心了。然后是时候进行定制了。

两个部署接口

两个重要的策略界面贴在这里,我们来回顾一下这两个长得很像的兄弟。

公共  接口 HandlerMethodArgumentResolver {
​ // 判断是否支持解析当前参数
​ boolean supportsParameter (MethodParameter 参数);
//解析当前参数
Object resolveArgument(MethodParameter 参数, @Nullable ModelAndViewContainer mavContainer, Native WebRequest webRequest, @可空的 WebDataBinderFactory (binderFactory) 抛出异常;
}
公共  接口 HandlerMethodReturnValueHandler {  ​ // 判断当前处理器是否可以处理当前方法的返回值
​ boolean supportsReturnType(MethodParameter returnType);
// 处理当前方法的返回值
void handleReturnValue( @Nullable Object returnValue, MethodParameter returnType ,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
抛出异常
;
}

策略模式是最明显的定制标记之一。我们也可以实施我们自己的策略,然后处理我们的参数。来吧,打开肝脏。

接口实现

代码表

code_file_list.png
MyCustomArgumentResolver:自定义请求参数解析器MyCustomReturnValueHandler:自定义返回值处理器MyResponse:返回值处理时使用的注释解决方案,analogy@ResponseBodyResolverHandler配置 :参数解析器和返回值处理器的配置 ResponseResut:具体返回值,想想你项目中各个接口的返回值是否和一个泛型Type javaToken2UserId:请求参数解析器使用的注解,类似于@RequestParam

以上是对各个职业的作用的简单介绍。定制的内容比较简单,主要是为了吸引别人。现在我们来谈谈这个自定义内容:

  1. 通过注解@TokenUserId将请求头中的token转换为userId。
  2. 标注注解@MyResponse注解,或者指定类型返回对象ResponseResult接口,使用我们自定义的接口进行处理,如果返回对象是 响应结果时,统一设置msg。

Token2UserId

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Token2UserId {
}

注解用于标记接口方法输入参数,以便自定义参数解析器能够识别。

MyCustomArgumentResolver

@Component
public classMyCustomArgumentResolver 实现 HandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(方法参数参数) {
returnparameter.getParameterAnnotation (Token2UserId.class) != null;
}

@Override
公共 对象resolveArgument(MethodParameter参数、ModelAndViewContainer mavContainer、NativeWebRequest webRequest、WebDataBinderFactorybinderFactory)抛出异常 {
字符串令牌 = webRequest.getHeader("令牌");
返回 getUserIdByToken(token);
}

private字符串getUserIdByToken(字符串令牌) {
//这里通过去数据库或服务器,可以将token转换成userId
return token + ”:100"
;
}
}

逻辑比较简单,如果标记了@Token2UserId 注解,然后使用当前解析器进行解析。流程也很简单,从header中取出token值,然后通过token获取userId并返回。

该类用 @Component 进行注释。虽然是由Spring管理,但仅此还不够。我们的解析器还不能生效。这里添加这个注解主要是提到这里可以注入一些其他的bean,比如RedisTemplate,用于用userId替换token。为了进行测试,我设置了 user=token+100

我的回复

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface 我的回复 {
}

注释用于标记方法,以便我们的返回值处理器可以识别它们。

响应结果

公共  响应结果<T> 实现 java.io.可序列化 {
私有 整数代码;
私有 字符串消息;
私有 T 数据;

公共 静态 响应结果 成功(T数据) {
响应结果 ret = new 响应结果<>();
ret.code = 0; www.sychzs.cn = 数据;
返回 ret;
}
}

看到这个内容并不是很熟悉了,我们一般在开发项目中,都会有一个统一的返回体,类似这样。

MyCustomReturnValueHandler

公共  MyCustomReturnValueHandler 实现   HandlerMethodReturnValueHandler {
@Override
public boolean 支持ReturnType(方法参数返回类型) {
//判断方法中是否包含自定义注解MyResonse或返回结果为指定类型
return returnType.getMethodAnnotation(MyResponse.class) ! = || ResponseResult.class.isAssignableFromreturnType。 getParameterType());
}

@Override
public void handleReturnValue(对象返回值、方法参数返回类型、ModelAndViewContainer mavContainer、NativeWebRequest webRequest) throws异常 {
//表示请求已处理完毕,spring稍后不会再处理
mavContainer.setRequestHandled() 真
);
if(返回值!= null && ResponseResult.class.isAssignableFrom( returnType.getParameterType())) {
字符串消息 = ((ResponseResult)returnValue).getCode() == 0 ? "成功" : "失败";
((ResponseResult)returnValue).setMsg(msg);
}
HttpServletResponse 响应 = webRequest. getNativeResponse(HttpServletResponse.class);
response.setCharacterEncoding("UTF-8");
response.setContentType(" application/json;charset=UTF-8");
System.out.println("由MyCustomReturnValueHandler处理的返回值。");
response.getWriter().println (JSON.toJSONString(returnValue));
}
}

自定义的返回值处理器,就没有加@Component注解哦。逻辑比较简单,如果方法包含@MyResponse注解,或者类型是ResponseResult及其子类,那么就采用我们的这个返回值处理器进行处理。

真正处理返回值的handlerReturnValue()方法中

  1. 我们首先标记请求已被处理(这个标记应该是第三次提到)。

  2. 然后我们判断如果返回类型是ResponseResult及其子类,我们根据代码设置msg信息。

  3. 最后将整个数据输出到响应输出流,完成本次请求。

ResolverHandler配置

@Configuration(proxyBeanMethods = false
公共 ResolverHandlerConfiguration {

@Bean
publicWebMvcConfigurermyWebMvcConfigurer(@Autowired MyCustomArgumentResolver myCustomArgumentResolver) {
回归 WebMvcConfigurer() {
@Override
public void addArgumentResolvers(列表 参数解析器) {
// 将自定义请求参数解析器添加到解析器列表中
argumentResolvers.add(0, myCustomArgumentResolver);
}

@覆盖
public void addReturnValueHandlers(列表处理程序) {
//自定义我们的返回值handler添加到返回值处理程序列表
handlers.add(0, new MyCustomReturnValueHandler());
}
};
}

}

自定义 请求参数解析器 返回值处理器 配置方法,虽然我们调用的方法是添加到第0个集合的位置,但它并不是真正的第一个。

添加逻辑

我们的解析器添加逻辑全部在RequestMappingHandlerAdapter#afterPropeties()方法中,在《Spring源码解析(第六弹)》中提到过,但不深入。这里特别注意,添加逻辑,先添加默认的,再添加自定义的,使用时不排序! ! !添加顺序是主要的! 所以这里我们实际上只是将第一个添加到自定义中。返回值处理程序也是如此。这是一个简单的代码。代码太长,影响阅读。逻辑是一样的。以解析器为例,只贴出解析器部分。

public void afterPropertiesSet() {
...
如果 (这个.argumentResolvers == null){
列表解析器 = getDefaultArgumentResolvers();
这个.argumentResolvers = HandlerMethodArgumentResolverComposite().addResolvers(解析器);
}
...
}

私有列表 getDefaultArgumentResolvers() {
List 解析器 = new ArrayList<>();
//这里会添加一堆默认的
...
//添加自定义
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(get CustomArgumentResolvers());
}
返回解析器;
}

测试接口

@Controller
公共 TestController {

@GetMapping("/testResolverHandler" )
@MyResponse
公共响应结果 testResolverHandler(@Token2UserId String userId){
返回 ResponseResult.success(userId);
}
}

这个地方需要注意。我使用@Controller而不是@RestController,并且该方法没有标记@ResponseBody注释

当我们使用自定义返回值处理时,如上所述,是默认优先级,不支持排序。因此,如果我们使用@ResponseBody,那么就会默认的返回值处理器已经处理完毕,而我们自定义的返回值处理器就没有轮到了!

结果调试,图有真相

获取http://localhost:8080/testResolverHandler
接受:*/*
令牌:abcd

相关文章