当前位置: 首页 > article >正文

Spring自定义参数解析器

在这篇文章中,我们认识了参数解析器和消息转换器,今天我们来自定义一个参数解析器。

自定义参数解析器

实现HandlerMethodArgumentResolver的类,并注册到Spring容器。

@Component//注册到Spring
public class UserArgResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    // 如果参数上有@User注解,并且参数类型是User或者其子类,则可以使用这个参数解析器
        return parameter.hasParameterAnnotation(User.class) && parameter.getParameterType().isAssignableFrom(UserInfo.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {
        final HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        final String userName = request.getHeader("userName");
        if (userName == null) {
            throw new RuntimeException("请求头中缺少用户信息");
        }
        final UserInfo user = new UserInfo();
        user.setName(userName);
        //返回值直接给Controller了
        return user;
    }
}

Spring Boot直接把解析器定义为Bean即可,如果是SpringMVC则需要这样注册

@Configuration
public class Config implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new UserArgResolver());
    }
}

定义接口

    @GetMapping("/test2")
    @ResponseBody
    public String test2(@User UserInfo userInfo) {
        System.out.println(userInfo.getName());
        return "ok";
    }

UserInfo

public class UserInfo {
    private String name;
    // get and set
}

注解

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({PARAMETER})
@Retention(RUNTIME)
@Documented
public @interface User {
}

我们的参数解析器是从请求头中解析信息,请求头中要有userName属性,不然会抛异常。请求方式如下:
在这里插入图片描述
此时Controller中的接口上可以成功接收参数解析器中解析到的UserInfo参数。

值得注意的是Spring Boot的参数解析是否生效和添加顺序也有关系,下面是RequestMappingHandlerAdapter中默认的添加顺序

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

		// 基于注解
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// 基于参数类型
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
		if (KotlinDetector.isKotlinPresent()) {
			resolvers.add(new ContinuationHandlerMethodArgumentResolver());
		}

		// 自定义
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// 兜底
		resolvers.add(new PrincipalMethodArgumentResolver());
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));

		return resolvers;
	}

可以看到有两个RequestParamMethodArgumentResolver,第一个必须明确使用@RequestParam才会起作用,第二个优先级在自定义之后.

请求头参数

    @GetMapping("/test3")
    @ResponseBody
    public String test3(@RequestHeader("name") String headerName) {
        System.out.println(headerName);
        return "ok";
    }

    @GetMapping("/test3")
    @ResponseBody
    public String test3(HttpHeaders headers) {
        System.out.println(headers.get("name"));
        return "ok";
    }

上面连中获取请求头的写法中,第一种使用注解方式是正确的,第二种写法使用的是MapMethodProcessor,是获取不到完整的请求头的。我们自定义一个基于类型的请求头参数解析器也没用,因为MapMethodProcessor优先级高于自定义的优先级。此时可以对RequestMappingHandlerAdapter的argumentResolvers

@Configuration
public class Config implements WebMvcConfigurer {
    @Bean
    public RequestMappingHandlerAdapter adapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
        //设置参数解析器
        final List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
        List<HandlerMethodArgumentResolver> list1 = new ArrayList<>(argumentResolvers.size() + 1);
        // 自定义解析器添加到第一个位置
        list1.add(0, new UserArgResolver());
        list1.addAll(argumentResolvers);
        requestMappingHandlerAdapter.setArgumentResolvers(list1);
        return requestMappingHandlerAdapter;
    }
}

不过一般没必要这样,我们可以通过其他方式获取请求头,比如从请求对象中获取

    @GetMapping("/name3")
    @ResponseBody
    public String name3(HttpServletRequest request) {
        request.getHeader("name");
        return "ok";
    }

http://www.kler.cn/a/318347.html

相关文章:

  • aws-athena查询语句总结
  • 【C++学习(37)】并发性模式:如生产者-消费者、读写锁等。 架构模式:如MVC、MVVM等。属于23 种设计模式吗? RAII 的关系?
  • 分享 pdf 转 word 的免费平台
  • ARM架构中断与异常向量表机制解析
  • 【Excel】身份证号最后一位“X”怎么计算
  • 解锁微前端的优秀库
  • weblogic中间件漏洞复现
  • M2型TAM靶向肽CRV; Ahx-CRVLRSGSC ;
  • 【系统架构设计师】设计模式的分类
  • Python 中自动打开网页并点击[自动化脚本],Selenium
  • [Python]案例驱动最佳入门:Python数据可视化在气候研究中的应用
  • C++——用带有默认参数的函数实现,求两个整数或三个整数中的最大数。
  • QT客户端发送HTTP请求此时服务器到底收到了哪些数据?
  • fastson与jackson重点笔记(包入门)
  • Cilium + ebpf 系列文章- XDP (eXpress data Path)(四)
  • 基于jsonpath的JSON数据查找
  • golang学习笔记1-go程序执行流程
  • element-plus表格操作
  • C++之哈希 --- 哈希的应用(位图布隆过滤器)
  • 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上)
  • 【软件测试】详解测试中常用的几种测试方法
  • 从更底层的角度理解网站的访问过程
  • 算法打卡:第十一章 图论part05
  • 关于Python升级以后脚本不能运行的问题
  • MongoDB-aggregate流式计算:去重操作
  • Linux下go环境安装、环境配置并执行第一个go程序