首先将结论写文章的最前面,一个项目中只能有一个继承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. c++ 重载、继承、多态

    一.重载 1.函数重载 在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数.类型或者顺序)必须不同.您不能仅通过返回类型的不同来重载函数. #include & ...

  2. Codeforces Round #628 (Div. 2)

    1325A - EhAb AnD gCd 题意:随意找两个数是他们的最大公约数 GCD 与最小公倍数 LCM 之和为所给定的值. 思路:找一下规律 ,假设所给的 数位n, 那么我们将n分成 1 ,n- ...

  3. 前端上传视频、图片、文件等大文件 组件Plupload使用指南

    demo:https://blog.csdn.net/qq_30100043/article/details/78491993 Plupload上传插件中文帮助文档网址:http://www.phpi ...

  4. SciPy - 正态性 与 KS 检验

    假设检验的基本思想 若对总体的某个假设是真实的,那么不利于或者不能支持这一假设的事件A在一次试验中是几乎不可能发生的:如果事件A真的发生了,则有理由怀疑这一假设的真实性,从而拒绝该假设: 假设检验实质 ...

  5. (js描述的)数据结构[链表](4)

    (js描述的)数据结构 [链表](4) 一.基本结构 二.想比于数组,链表的一些优点 1.内存空间不是必须连续的,可以充分利用计算机的内存,事项灵活的内存动态管理. 2.链表不必再创建时就确定大小,并 ...

  6. Genetic CNN: 经典NAS算法,遗传算法的标准套用 | ICCV 2017

    论文将标准的遗传算法应用到神经网络结构搜索中,首先对网络进行编码表示,然后进行遗传操作,整体方法十分简洁,搜索空间设计的十分简单,基本相当于只搜索节点间的连接方式,但是效果还是挺不错的,十分值得学习 ...

  7. C语言数据结构队列

    #include<stdio.h>#include<stdlib.h>struct Node { int data;  Node *next;};struct Queue { ...

  8. redis 正确实现分布式锁的正确方式

    前言 最近在自己所管理的项目中,发现redis加锁的方式不对,在高并发的情况有问题.故在网上找搜索了一把相关资料.发现好多都是互相抄袭的,很多都是有缺陷的.好多还在用redis 的 setnx命令来实 ...

  9. JPA入门例子(采用JPA的hibernate实现版本) --- 会伴随 配置文件:persistence.xml

    JPA入门例子(采用JPA的hibernate实现版本) 分类: j2se2011-03-30 16:09 45838人阅读 评论(9) 收藏 举报 jpahibernate数据库jdbcjava框架 ...

  10. Spring温习(1)--最基础的示例

    Spring温习(1)--最基础的示例 博客分类: 框架-Spring专栏 SpringXMLBeanWebDAO 从现在开始,我将从Spring为起点,逐步复习几大框架各方面的知识,以便今后查看使用 ...