Spring Boot中只能有一个WebMvcConfigurationSupport配置类
首先将结论写文章的最前面,一个项目中只能有一个继承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修饰的方法。
整个流程如下:
- 扫描到CorsConfig类,生成Bean对象时调用父类中的被Bean修饰的方法
- 其中某些方法调用了被子类重写的
addCorsMappings(CorsRegistry registry)
方法,完成了自定义配置 - 负责管理映射资源的
resourceHandlerMapping
方法在此时也被调用了,但是在CorsConfig类中没有对其调用的addResourceHandlers
重写,实际上调用了一个空实现。 - 扫描到ImageConfig类,生成Bean对象时发现其中从父类继承的Bean方法已经生成实例了,于是不再调用
resourceHandlerMapping
,因此重写的addResourceHandlers
方法也就不在有机会运行。
总结
Spring Boot中只能有一个WebMvcConfigurationSupport配置类是真正起作用的,对于这个问题,其实可以通过implements WebMvcConfigurer
来解决,多个不同的类实现这个接口后的配置都可以正常运行。
事实上,对于映射资源,Spring Boot的官方文档给出的例子也是通过实现接口完成的。从这次的经历可以看出在写代码的过程中多阅读官方文档可以少走很多弯路,比各类博客文章的教程质量也要更高。另外,在写代码时遇到了问题,除了解决问题本身,了解产生问题的原因也是非常重要的,在这个过程中可以对使用的框架的运行流程更加熟悉。
Spring Boot中只能有一个WebMvcConfigurationSupport配置类的更多相关文章
- 兼容 Spring Boot 1.x 和 2.x 配置类参数绑定的工具类 SpringBootBindUtil
为了让我提供的通用 Mapper 的 boot-starter 同时兼容 Spring Boot 1.x 和 2.x,增加了这么一个工具类. 在 Spring Boot 中,能够直接注入 XXProp ...
- Spring Boot 中 Druid 的监控页面配置
Druid的性能相比HikariCp等其他数据库连接池有一定的差距,但是数据库的相关属性的监控,别的连接池可能还追不上,如图: 今天写一下 Spring Boot 中监控页面的配置,我是直接将seat ...
- 曹工谈Spring Boot:Spring boot中怎么进行外部化配置,一不留神摔一跤;一路debug,原来是我太年轻了
spring boot中怎么进行外部化配置,一不留神摔一跤:一路debug,原来是我太年轻了 背景 我们公司这边,目前都是spring boot项目,没有引入spring cloud config,也 ...
- java中只能有一个实例的类的创建
Java中,如果我们创建一个类,想让这个类只有一个对象,那么我们可以 1:把该类的构造方法设计为private 2:在该类中定义一个static方法,在该方法中创建对象 package test; / ...
- Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...
- Spring Boot中的缓存支持(一)注解配置与EhCache使用
Spring Boot中的缓存支持(一)注解配置与EhCache使用 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决 ...
- Spring Boot 中自定义 SpringMVC 配置,到底继承谁哪一个类或则接口?
看了这篇文章,写的非常的言简意赅,特此记录下: 1.Spring Boot 1.x 中,自定义 SpringMVC 配置可以通过继承 WebMvcConfigurerAdapter 来实现. 2.Sp ...
- Spring Boot中使用EhCache实现缓存支持
SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...
- Spring Boot中使用MongoDB数据库
前段时间分享了关于Spring Boot中使用Redis的文章,除了Redis之后,我们在互联网产品中还经常会用到另外一款著名的NoSQL数据库MongoDB. 下面就来简单介绍一下MongoDB,并 ...
随机推荐
- RuntimeError: PyTorch was compiled without NumPy support
原因:Pytorch和Numpy版本不匹配 查看自己Pytorch和Numpy版本 (1)执行[pip show torch]和[pip show numpy]查看版本信息(可通过[pip -h]查看 ...
- 一文读懂什么是CA证书
Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable ...
- Sql练习201908200916
表结构: 一条sql修改多条数据(Sql server),oracle,mysql请自行尝试: ; go 完成.
- Python执行js之PyexecJs
利用Python执行js 爬虫中会经常碰到JS加密,当我们找到他加密的JS代码之后我们需要获取它的返回值,python虽然可以模仿js写一个python版本的加密,但是这样有点费时间,也有点费头发~ ...
- 怎么处理使用UINavigation(导航控制器时) UIScrollView及其子类UITableView、UICollectionView可能出现的向下偏移64Px或者顶部对齐等问题
前言 近期在做项目时遇到了好几起由于自动偏移或则没有自动偏移而导致的界面布局问题,尤其是在昨晚新版本赶上IOS9系统升级的时候,刚升级完了后就发现项目里面很多使用UINavgati ...
- 通过jsDelivr + github 搭建一个简易图床
应用场景: 在大型项目里需要很多图片时,不会直接把图片存储在项目文件夹里,也不推荐直接用数据库存储,而是用第三方存储,cdn,也可以自己搭个存储图片的服务器,等等方式,如果时自己练练手,做做博客,写写 ...
- Asp.Net.Core WebApi 版本控制
前言 在后端Api的开发过程中,无法避免的会遇到接口迭代的过程,如何保证新老接口的共存和接口的向前的兼容呢,这时候就需要对Api进行版本的控制,那如何优雅的控制Api的版本呢? 开始 Microsof ...
- SWUST OJ 1075 求最小生成树(Prim算法)
求最小生成树(Prim算法) 我对提示代码做了简要分析,提示代码大致写了以下几个内容 给了几个基础的工具,邻接表记录图的一个的结构体,记录Prim算法中最近的边的结构体,记录目标边的结构体(始末点,值 ...
- vue渲染src
- JavaScript中数组的两种排序方法详解(冒泡排序和选择排序)
一.冒泡排序的原理(从小到大) 相邻两个数进行比较,如果前一个数大于后一个数,那么就交换,否则不交换 原理剖析 比如有一组含有6个数字的数:5.3.7.2.1.6一共6个数字,做5次循环,每次循环相邻 ...