自学内容网 自学内容网

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";
    }

原文地址:https://blog.csdn.net/weixin_41535316/article/details/142417257

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!