首先将结论写文章的最前面,一个项目中只能有一个继承WebMvcConfigurationSupport的@Configuration类(使用@EnableMvc效果相同),如果存在多个这样的类,只有一个配置可以生效。推荐使用

implements WebMvcConfigurer 的方法自定义mvc配置。

背景

项目中的一个模块需要实现上传图片后通过url访问保存在本地上的图片的功能,在SpringBoot 系列教程(十八):SpringBoot通过url访问获取内部或者外部磁盘图片中详细介绍了各种方法,最后我采用了方式三中介绍的直接继承WebMvcConfigurationSupport来实现这一功能。

场景复现

首先按照文章介绍的方法实现配置类

@Configuration
public class ImageConfig extends WebMvcConfigurationSupport { @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**")
.addResourceLocations("file:./localdata/images/");
}
}

重新启动项目以后尝试访问图片url,但是返回了404错误



经过一番排查,我发现重载的这段方法在Spring Boot启动过程中实际并没有执行,但之前添加的一个跨域的mvc配置却是正确加载了。

这是跨域配置类的实现

@Configuration
public class CorsConfig extends WebMvcConfigurationSupport { @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600);
} }

两个类都继承了 WebMvcConfigurationSupport 并重写了需要自定义配置的方法,但一个生效了另一个却没有。于是我猜测只能有一个继承 WebMvcConfigurationSupport 的配置类,为了验证我的猜测,我将跨域配置的@Configuration注解删去,只保留ImageConfig的配置,果然可以正常访问图片了!到这里基本可以确定,在Spring Boot的启动过程中,被@Configuration注解的所有类中只有一个WebMvcConfigurationSupport子类的自定义配置可以被正确加载。很容易可以想到,将两段方法写在同一个类中就可以解决这样的问题。那么为什么会出现这样的情况呢?我用类似的关键字搜索,发现同样有人遇到了类似的问题:WebMvcConfigurationSupport没有生效的问题。但是却没有一篇文章讲清楚了原因,于是我决定探索一下其中的奥秘。

原因探索

显然,要找到@Configuration类不能正确加载,就要从Spring Boot如何加载mvc配置入手,但是这方面我不是很了解,于是我在代码中抛出一个throw new NullPointerException();来看以下调用堆栈

org.springframework.beans.factory.BeanCreationException: Error creating bean with
name 'resourceHandlerMapping' defined in class path resource
[test/config/ImageConfig.class]: Bean instantiation via factory method failed; nested
exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate
[org.springframework.web.servlet.HandlerMapping]: Factory method
'resourceHandlerMapping' threw exception; nested exception is
java.lang.NullPointerException

可以看到,是在创建'resourceHandlerMapping'这个bean对象的时候抛出了异常,那么自定义配置的代码也一定是在这个时候被调用的。这个Bean对象正是在被继承的WebMvcConfigurationSupport类中定义的。于是我又打开了相关的源码

@Bean
@Nullable
public HandlerMapping resourceHandlerMapping(...) {
...
this.addResourceHandlers(registry);
...
}
}

忽略掉无关的代码,可以看到这个被Bean修饰的方法调用了addResourceHandlers(registry)方法,而这个方法正是继承了这个类后重写的方法,我们用自己自定义的配置重写这个方法就可以改变配置的行为。这样的设计其实就是设计模式中的模板方法模式,在父类中定义方法的框架然后通过钩子函数改变一些特定的步骤。

再回到原本的问题上来,其实当看到这个方法被@Bean修饰之后其实我已经心中又有数了:在Spring Boot中,一个被@Bean修饰的方法在启动过程中会被调用生成Bean对象存放在IoC容器中(前提是这个类本身已经被Spring Boot管理生命周期),也就是说通过继承WebMvcConfigurationSupport自定义的配置方法是在生成父类中定义的@Bean方法时被调用的,而两个配置类中的Bean对象的id是一样的(来自同一个父类相同的方法),也就是说在生成第二个配置类的对象的时候不会再调用其中被@Bean修饰的方法。

整个流程如下:

  1. 扫描到CorsConfig类,生成Bean对象时调用父类中的被Bean修饰的方法
  2. 其中某些方法调用了被子类重写的addCorsMappings(CorsRegistry registry)方法,完成了自定义配置
  3. 负责管理映射资源的resourceHandlerMapping方法在此时也被调用了,但是在CorsConfig类中没有对其调用的addResourceHandlers重写,实际上调用了一个空实现。
  4. 扫描到ImageConfig类,生成Bean对象时发现其中从父类继承的Bean方法已经生成实例了,于是不再调用resourceHandlerMapping,因此重写的addResourceHandlers方法也就不在有机会运行。

总结

Spring Boot中只能有一个WebMvcConfigurationSupport配置类是真正起作用的,对于这个问题,其实可以通过implements WebMvcConfigurer来解决,多个不同的类实现这个接口后的配置都可以正常运行。

事实上,对于映射资源,Spring Boot的官方文档给出的例子也是通过实现接口完成的。从这次的经历可以看出在写代码的过程中多阅读官方文档可以少走很多弯路,比各类博客文章的教程质量也要更高。另外,在写代码时遇到了问题,除了解决问题本身,了解产生问题的原因也是非常重要的,在这个过程中可以对使用的框架的运行流程更加熟悉。

Spring Boot中只能有一个WebMvcConfigurationSupport配置类的更多相关文章

  1. 兼容 Spring Boot 1.x 和 2.x 配置类参数绑定的工具类 SpringBootBindUtil

    为了让我提供的通用 Mapper 的 boot-starter 同时兼容 Spring Boot 1.x 和 2.x,增加了这么一个工具类. 在 Spring Boot 中,能够直接注入 XXProp ...

  2. Spring Boot 中 Druid 的监控页面配置

    Druid的性能相比HikariCp等其他数据库连接池有一定的差距,但是数据库的相关属性的监控,别的连接池可能还追不上,如图: 今天写一下 Spring Boot 中监控页面的配置,我是直接将seat ...

  3. 曹工谈Spring Boot:Spring boot中怎么进行外部化配置,一不留神摔一跤;一路debug,原来是我太年轻了

    spring boot中怎么进行外部化配置,一不留神摔一跤:一路debug,原来是我太年轻了 背景 我们公司这边,目前都是spring boot项目,没有引入spring cloud config,也 ...

  4. java中只能有一个实例的类的创建

    Java中,如果我们创建一个类,想让这个类只有一个对象,那么我们可以 1:把该类的构造方法设计为private 2:在该类中定义一个static方法,在该方法中创建对象 package test; / ...

  5. Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置

    用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...

  6. Spring Boot中的缓存支持(一)注解配置与EhCache使用

    Spring Boot中的缓存支持(一)注解配置与EhCache使用 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决 ...

  7. Spring Boot 中自定义 SpringMVC 配置,到底继承谁哪一个类或则接口?

    看了这篇文章,写的非常的言简意赅,特此记录下: 1.Spring Boot 1.x 中,自定义 SpringMVC 配置可以通过继承 WebMvcConfigurerAdapter 来实现. 2.Sp ...

  8. Spring Boot中使用EhCache实现缓存支持

     SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...

  9. Spring Boot中使用MongoDB数据库

    前段时间分享了关于Spring Boot中使用Redis的文章,除了Redis之后,我们在互联网产品中还经常会用到另外一款著名的NoSQL数据库MongoDB. 下面就来简单介绍一下MongoDB,并 ...

随机推荐

  1. Go语言库系列之aurora

    背景介绍 今天跟大家推荐一款可以给终端输出上色的工具--aurora. 极速上手 准备工作 初始化项目 go mod init aurora 演示项目结构 . ├── go.mod ├── go.su ...

  2. 关于HashCode和equals方法在HashSet中的使用

    Object类是类层次结构的根类,故所有的类都是先该类的方法,其中HashCode()和equals()方法也是该类的方法. 1.HashCode()方法 Object类中HashCode()方法实现 ...

  3. 如何让Java应用成为杀不死的小强?(下篇)

    各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 经过前两期的铺垫及烧脑的分享,我们大概对「如何实现 Java 应用进程的状态监控,如果被监控的进程 down 掉,是否 ...

  4. 基于OAuth2.0的token无感知刷新

    目前手头的vue项目关于权限一块有一个需求,其实架构师很早就要求我做了,但是由于这个紧急程度不是很高,最近临近项目上线,我才想起,于是赶紧补上这个功能.这个项目是基于OAuth2.0认证,需要在每个请 ...

  5. psutil运维必会模块

    目录 psutil介绍 安装psutil 获取CPU信息 获取内存信息 获取磁盘信息 获取网络信息 获取进程信息 psutil介绍 用Python来编写脚本简化日常的运维工作是Python的一个重要用 ...

  6. json的fromjson的方法使用。可以在volley中进行使用

    Gson提供了fromJson()方法来实现从Json相关对象到Java实体的方法. 在日常应用中,我们一般都会碰到两种情况,转成单一实体对象和转换成对象列表或者其他结构. 先来看第一种: 比如jso ...

  7. 解决:docker-compose端口绑定

    docker-compose 进程绑定 Bind for 0.0.0.0:3825 failed: port is already allocated 查看进程发现有进程在关闭后继续进行 docker ...

  8. JS 浏览器BOM-->简介和属性

    1.简介: BOM:浏览器对象模型(Browser Object Model),是一个用于访问浏览器和计算机屏幕的对象集合.我们可以通过全局对象window来访问这些对象.  2.属性 window. ...

  9. 在SpringBoot中使用SpringSecurity

    @ 目录 提出一个需求 解决方案: 使用SpringSecurity进行解决 SpringSecurity和SpringBoot结合 1. 首先在pom.xml中引入依赖: 2. 配置用户角色和接口的 ...

  10. Dempster–Shafer theory(D-S证据理论)初探

    1. 证据理论的发展历程 Dempster在1967年的文献<多值映射导致的上下文概率>中提出上.下概率的概念,并在一系列关于上下概率的文献中进行了拓展和应用,其后又在文献<贝叶斯推 ...