【开发者必备】Spring Boot 2.7.x:WebMvcConfigurer配置手册来了(五)!

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

上一期我们介绍了addCorsMappingsaddViewControllers以及configureViewResolvers,分别用来处理跨域、视图转发控制以及视图解析。我们继续WebMvcConfigurer配置的分享,这一期了解两个方法:

  • addArgumentResolvers
  • addReturnValueHandlers

分别用来处理参数解析以及返回值的处理。

02 方法11

addArgumentResolvers

java 复制代码
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}

作用:添加自定义方法参数解析器。

使用场景

  • 解析自定义注解参数
  • 统一用户信息提取
  • 特殊参数类型处理

2.1 使用说明

这个方法用起来比较简单,可以针对Web提交的数据,进行预处理,处理成符合预期的参数。也可以通过传递的参数,过滤掉垃圾数据,防止无意义的数据在网络传播或者入库。

通过方法我们可以看到,需要我们定义一个HandlerMethodArgumentResolver即可,然后放到集合中去,由Spring统一管理。

  • supportsParameter:处理参数的条件
  • resolveArgument:重新封装参数

2.2 自定义参数解析器

java 复制代码
@Slf4j
public class WjsonArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(
        MethodParameter parameter) {
        log.info("supportsParameter: ...");
        return parameter.getParameterType()
                .equals(Wjson.class);
    }

    @Override
    public Object resolveArgument(
            MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) 
        	throws Exception {
        log.info("resolveArgument: ...");
        String content = webRequest.getParameter("content");
        return new Wjson(content, "v1");
    }
}

定义的方法通过判断接收的参数类型是不是Wjson(自定义的类),如果是,就处理参数,额外在参数增加版本号v1

2.3 配置

java 复制代码
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    resolvers.add(new WjsonArgumentResolver());
}

2.4 控制层与测试

java 复制代码
@GetMapping("/testWjson")
public void testWjson(Wjson wjson) {
    log.info("testWjson: wjson={} ", wjson);
}

传递参数时,无论版本号传或者不传,都会被处理成v1

测试结果:

03 方法12

addReturnValueHandlers

java 复制代码
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}

作用:添加自定义返回值处理器。

使用场景

  • 统一响应格式封装
  • 特殊返回值类型处理
  • 响应数据加密

3.1 使用说明

这个方法配置方式完全和addArgumentResolvers相同,但是在定义过程中可能会会被系统自带的HandlerMethodReturnValueHandler优先取代。这个我们在后面会解释原因。

我们同样需要实现HandlerMethodReturnValueHandler处理自己的响应结果。

3.2 自定义返回值处理器

java 复制代码
@Slf4j
public class WjsonReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        log.info("supportsReturnType 。。。。。");
        return returnType.getParameterType()
                .equals(Wjson.class);
    }

    @Override
    public void handleReturnValue(
        Object returnValue,
        MethodParameter returnType,
        ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest)
            throws Exception {
        log.info("handleReturnValue 。。。。。");

        // 标记请求已处理,避免视图解析
        mavContainer.setRequestHandled(true);

        HttpServletResponse response = webRequest
            .getNativeResponse(HttpServletResponse.class);
        response.setContentType(MediaType.TEXT_HTML_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write("<h1>测试</h1>");
    }
}

假设我们要拦截返回值为Wjson的方法,统一跳转自定义页面。其中mavContainer.setRequestHandled(true);非常重要,表示请求已经处理,不会再次被框架处理。

3.3 配置

java 复制代码
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
    handlers.add(new WjsonReturnValueHandler());
}

3.4 控制层与测试

java 复制代码
@GetMapping("/testWjsonReturn")
public Wjson testWjsonReturn() {
    return new Wjson("test", "v2");
}

注意这里的方法没有加@ResponseBody注解,加了的话会导致配置不生效。

测试结果:

3.5 注意事项

当我们的返回值为Void.classString.class或者加了@ResponseBody注解,我们的配置就不会生效了。这是小编测试时遇到的最大问题。

源码的关键类:

  • RequestMappingHandlerAdapter
  • HandlerMethodReturnValueHandlerComposite

通过源码追踪发现:

自定义的WjsonReturnValueHandler排在后面,前面的有RequestResponseBodyMethodProcessor,专门处理@ResponseBody注解。

ViewNameMethodReturnValueHandler专门处理返回值为Void.classString.class的方法:

知道了原因,下面就是如何解决?

解决的思路很简单,就是将自定义的处理器放在前面即可。通过list.add(0, xx)是行不通的,因为配置的List是定制的处理器。

而定制的处理器会加载在框架默认的之后。

所以我们要拿到处理器的集合并将我们自定的加载在前民就好了。继续跟踪源码:

发现获取的处理器集合是个不可变结合,无法做添加处理。那我们就获取到框架自带的处理器,重新设置到RequestMappingHandlerAdapter里面即可。

最终方法

java 复制代码
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;

@PostConstruct
public void init() {
    List<HandlerMethodReturnValueHandler> returnValueHandlers = handlerAdapter.getReturnValueHandlers();
    List<HandlerMethodReturnValueHandler> list = new ArrayList<>(returnValueHandlers);
    list.add(0, new WjsonReturnValueHandler());
    handlerAdapter.setReturnValueHandlers(list);
}

这种处理方法,无需再使用addReturnValueHandlers处理了。如图,已经加载了最前面。

04 小结

关于类似的配置下一期还会介绍两个。集合的配置能不能通过list.add(0, xxx),取决于框架的取值。所以当我们添加的配置不是能生效时,多半原因是和框架自定义的冲突导致的。需要我们结合源码,调整顺序就可以解决。这一期就介绍到这里了。

相关推荐
知兀3 小时前
【Java】@Autowired警告问题,使用@RequiredArgsConstructor
java
Value_Think_Power3 小时前
Dapr pub/sub
后端
间彧3 小时前
Fastjson Map转JSON字符串API详解与项目实战
后端
Value_Think_Power3 小时前
dapr::Listiner
后端
闲云散3 小时前
WebClient 简述
java·后端
狗头大军之江苏分军4 小时前
请不要在感情里丢掉你的“我”
前端·后端
wudl55664 小时前
JDK 21性能优化详解
java·开发语言·性能优化
CodeAmaz4 小时前
ELK(Elasticsearch + Logstash + Kibana + Filebeat)采集方案
java·elk·elasticsearch·1024程序员节
864记忆4 小时前
项目名称:烟酒进销存管理系统
java