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

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

01 引言

上一节介绍了configurePathMatchconfigureContentNegotiation的使用方法。

这一节,我们继续了解三个配置:

  • configureAsyncSupport
  • configureDefaultServletHandling
  • addFormatters

在学习配置的时候,很多配置其实都是框架预留的钩子或者为了兼容老的项目而设置的。正常使用SpringBoot项目时,可能都用不上。

02 方法3

configureAsyncSupport

java 复制代码
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}

作用:配置 Spring MVC 的异步请求处理。

使用场景

  • 控制异步请求的超时时间
  • 异步请求异常,降级处理
  • 监控异步请求的耗时

2.1 配置

配置的详情取决于AsyncSupportConfigurer能够做什么?我们来看看他的源码:

从配置中我们可以看到最多也就操作图中的方法。方法中还需要设置一个TaskExecutor也就是我们所说的线程池。

java 复制代码
@Bean
public AsyncTaskExecutor asyncTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(10);
    executor.setQueueCapacity(25);
    executor.setThreadNamePrefix("xxx-test-async-");
    return executor;
}

@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    // 超时时间
    configurer.setDefaultTimeout(2000);
    // 设置执行任务:也可以不设置,使用默认的
    configurer.setTaskExecutor(asyncTaskExecutor());
    // 注册Callable拦截器
    configurer.registerCallableInterceptors(new CallableProcessingInterceptor(){

        @Override
        public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception {
            log.info("beforeConcurrentHandling 执行了...");
        }

        @Override
        public <T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception {
            log.info("preProcess 执行了...");
        }

        @Override
        public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) throws Exception {
            log.info("postProcess 执行了...");
        }

        @Override
        public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception {
            log.info("afterCompletion 执行了...");
        }

        @Override
        public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
            log.info("handleTimeout 执行了...");
            return "test";
        }
    });
}

2.2 使用注意事项

配置项里面最重要的就是注册Callablel拦截器,而Callable拦截器是针对项目中方法返回值是Callable<T>的所有的方法。无论你的方法写在控制层还是服务层,都会生效。

handleTimeouthandleError的返回值是影响方法的的返回的,可以做到服务降级。

2.3 拦截方法

java 复制代码
@GetMapping("/fooTest")
public Callable<String> fooTest() {
    return () -> {
        try {
            Thread.sleep(3000);
            log.info("睡眠3s后, 返回数据 ...");
        } catch (InterruptedException e) {
            // throw new RuntimeException(e);
        }
        return "success";
    };
}

2.4 测试

从执行的结果来看:

preProcesspostProcess使用了配置的线程。返回的结果因为超时,将success变成了test,实现了类似服务降级的效果。

简单来说,就是一个拦截器,拦截器能做的,他都能做。只不过拦截的方法不同而已。

03 方法4

configureDefaultServletHandling

java 复制代码
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}

主要作用就是启用默认Servlet 处理或者指定默认的Servlet名称。

java 复制代码
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    // 启用默认 Servlet 处理
    // configurer.enable("default");
    
    // 指定自定义的默认 Servlet 名称
    configurer.enable("customDefaultServlet");
}

Spring Boot 中,通常不需要显式启用默认 Servlet,因为自动配置已经处理了大部分情况。

有兴趣的朋友可以深挖一下呀。

04 方法5

addFormatters

作用:添加自定义格式化器,用于参数绑定和显示。

java 复制代码
default void addFormatters(FormatterRegistry registry) {
}

FormatterRegistry主要用来注册Formatter的,而Formatter继承自Printer<T>Parser<T>

当然也可以单独设置Printer<T>Parser<T>.

4.1 配置Parser

我们先来注册一个日期解析器,将字符串转化成日期。这个当然在前面的文章中通过@InitBinder注解来实现,如图:

我们今天换一种方式实现,先定义一个Parse

java 复制代码
public class DateParse implements Parser<Date> {

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        return DateUtil.parse(text, "yyyy-MM-dd HH:mm:ss");
    }
}

配置

java 复制代码
@Override
public void addFormatters(FormatterRegistry registry) {
    registry.addParser(new DateParse());
}

效果

对比未加配置之前接受日期类型的报错:

当然框架为我们提供了现成的DateFormatter,我们直接使用即可。

4.2 配置Printer

Parser用来解析参数,而Printer接口顾名思义,用户参数的展示。Printer用起来不在顺手,需要结合Parser一起使用。所以我们要直接定义Formatter

下面来实现一个手机号脱敏的效果:

java 复制代码
@Component
public class OrderFormatter implements Formatter<Order> {
    @Override
    public Order parse(String text, Locale locale) throws ParseException {
        return new Order(null, text);
    }

    @Override
    public String print(Order object, Locale locale) {
        return object.getTestStr().substring(0, 1) + "***" +object.getTestStr().substring(object.getTestStr().length() - 1);
    }
}

这里需要主要的是:

  • ① 当前类必须被Spring管理
  • ② 这里的Order并没有实际的意义,相当于一个临时的容器,用来管理参数
  • parse用来接收数据,而print用来输出数据

配置

java 复制代码
@Autowired
OrderFormatter orderFormatter;

@Override
public void addFormatters(FormatterRegistry registry) {
    // registry.addParser(new DateParse());
    registry.addFormatterForFieldType(String.class, orderFormatter);
}

这里的String.class表示处理String类型的参数。

案例

java 复制代码
@GetMapping("/testFormatter")
public void testFormatter(Date date, String test) {
    log.info("testFormatter param: date={}, test={}", date, test);
}

4.3 源码说明

源代码跟下来,parser解析完成之后才会到this.conversionService.convert(),该方法进入就是进入Print方法。

这路要注意的是:并不是所有的方法都会走Print方法。解析的结果类型和目标类型不一致时,才会触发此方法。

如案例,String类型被解析成Order类型,而目标类型又是String,所以才会生效。

4.4 额外说明

这里的Print大多数不需要配置,因为这里类似序列化,而序列化有框架自带的序列化工具,可能会忽略这里的配置。在使用此方法时,需要好好测试。有更好用的方法,评论区讨论。

05 小结

每一个方法深挖都会有很对子方法,在学习这些方法时,先搞清楚大方向,然后慢慢深入。这些方法的日常使用可能仅仅只配置一次,后续再也不会去改动了。很容易忘记。

相关推荐
杨筱毅5 小时前
【Android】Handler/Looper机制相关的类图和流程图
android·java·流程图
程序员飞哥5 小时前
别再说“对接接口没技术含量了”,这才是高手的打开方式!
后端·程序员
DokiDoki之父5 小时前
Spring—容器
java·后端·spring
一个龙的传说5 小时前
springboot优雅停止的流程梳理
java·spring boot·rpc
摇滚侠5 小时前
Spring Boot 3零基础教程,WEB 开发 国际化 Spring Boot + Thymeleaf 笔记45
spring boot·笔记·后端
间彧5 小时前
Java AQS详解与项目实战
后端
golang学习记6 小时前
性能飙升4倍,苹果刚发布的M5给人看呆了
人工智能·后端
搬砖的工人6 小时前
记录WinFrom 使用 Autoupdater.NET.Official 进行软件升级更新
java·前端·.net
xiezhr6 小时前
上班是为了更好的生活,不要本末倒置了~
程序员