为什么我们的项目里出现两个配置类继承WebMvcConfigurationSupport时,只有一个会生效。我在网上找了半天都是说结果的,没有人分析源码到底是为啥,博主准备讲解一下,希望可以帮到大家!

  大家基本遇到过一种情况,就是我配置类中已经配置了,为什么就是没有生效呢?其中一种原因就是,自己写的配置类也继承了WebMvcConfigurationSupport,当项目出现两个配置类都继承该类时,只会讲第一个配置类生效,至于为什么,就是今天博主需要讲解的,我们必须了解一些springboot的bean的创建过程也就是其生命周期:

  https://www.processon.com/view/link/5f704050f346fb166d0f3e3c

  虽然画的比较简单,有许多细节都没有解析,但是对于当前我们的话题来讲已经基本可以了;

  第一步:我们的配置类是从哪里开始创建解析的:大家可以看到图示bean的流程中doProcessConfigurationClass(configClass, sourceClass, filter);方法,我们看一下是如何调用它 的:

 1  protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
2 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
3 return;
4 }
5
6 ConfigurationClass existingClass = this.configurationClasses.get(configClass);
7 if (existingClass != null) {
8 if (configClass.isImported()) {
9 if (existingClass.isImported()) {
10 existingClass.mergeImportedBy(configClass);
11 }
12 // Otherwise ignore new imported config class; existing non-imported class overrides it.
13 return;
14 }
15 else {
16 // Explicit bean definition found, probably replacing an import.
17 // Let's remove the old one and go with the new one.
18 this.configurationClasses.remove(configClass);
19 this.knownSuperclasses.values().removeIf(configClass::equals);
20 }
21 }
22
23 // Recursively process the configuration class and its superclass hierarchy.
24 SourceClass sourceClass = asSourceClass(configClass, filter);
25 do {
26 //从这里开始解析我们的当前配置类
27 sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
28 }
29 while (sourceClass != null);
30
31 this.configurationClasses.put(configClass, configClass);
32 }

  这里可以看到一个while循环,为什么要这么设计呢?我们再看看doProcessConfigurationClass(configClass, sourceClass, filter);方法的源码

 1 protected final SourceClass doProcessConfigurationClass(
2 ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
3 throws IOException {
4
5 if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
6 // Recursively process any member (nested) classes first
7 processMemberClasses(configClass, sourceClass, filter);
8 }
9
10 // Process any @PropertySource annotations
11 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
12 sourceClass.getMetadata(), PropertySources.class,
13 org.springframework.context.annotation.PropertySource.class)) {
14 if (this.environment instanceof ConfigurableEnvironment) {
15 processPropertySource(propertySource);
16 }
17 else {
18 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
19 "]. Reason: Environment must implement ConfigurableEnvironment");
20 }
21 }
22
23 // Process any @ComponentScan annotations
24 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
25 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
26 if (!componentScans.isEmpty() &&
27 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
28 for (AnnotationAttributes componentScan : componentScans) {
29 // The config class is annotated with @ComponentScan -> perform the scan immediately
30 Set<BeanDefinitionHolder> scannedBeanDefinitions =
31 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
32 // Check the set of scanned definitions for any further config classes and parse recursively if needed
33 for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
34 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
35 if (bdCand == null) {
36 bdCand = holder.getBeanDefinition();
37 }
38 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
39 parse(bdCand.getBeanClassName(), holder.getBeanName());
40 }
41 }
42 }
43 }
44
45 // Process any @Import annotations
46 processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
47
48 // Process any @ImportResource annotations
49 AnnotationAttributes importResource =
50 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
51 if (importResource != null) {
52 String[] resources = importResource.getStringArray("locations");
53 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
54 for (String resource : resources) {
55 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
56 configClass.addImportedResource(resolvedResource, readerClass);
57 }
58 }
59 //这里也很重要,这里开始会解析当前配置类里的bean,然后解析父类里面的bean,就是这里才会把WebMvcConfigurationSupport的所有bean
60 //都解析出来并添加到configClass里面,不管解析当前类还是父类,configClass都是自己当前的配置类,所以WebMvcConfigurationSupport
61 // Process individual @Bean methods
62 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
63 for (MethodMetadata methodMetadata : beanMethods) {
64 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
65 }
66
67 // Process default methods on interfaces
68 processInterfaces(configClass, sourceClass);
69
70 //最主要的就是这里,解析当前类的父类
71 // Process superclass, if any
72 if (sourceClass.getMetadata().hasSuperClass()) {
73 String superclass = sourceClass.getMetadata().getSuperClassName();
74 if (superclass != null && !superclass.startsWith("java") &&
75 !this.knownSuperclasses.containsKey(superclass)) {
76 //如果我们第一个继承了WebMvcConfigurationSupport的配置类,已经被扫描到,就会添加一个map缓存,
77 //下一个也继承了WebMvcConfigurationSupport的配置类,将不在解析,直接返回null。结束循环,这也是外面一层为什么要添加while循环
78 this.knownSuperclasses.put(superclass, configClass);
79 // Superclass found, return its annotation metadata and recurse
80 return sourceClass.getSuperClass();
81 }
82 }
83
84 // No superclass -> processing is complete
85 return null;

  所以就现在来讲,基本已经决定了,解析第一个配置类的时候,第二个配置类重写的任何方法基本没什么用了,因为父类所有的bean已经在第一个配置类中解析扫描到了,就剩下如何去创建bean了。我们再继续往下看会更明白;

  第二步:现在当所有bean已经扫描到,并且bean定义已经完成,该开始实例化了,看一下createBeanInstance的创建过程,最后生成的时候会找到 factoryBean也就是我们自己的配置类

 1 private Object instantiate(String beanName, RootBeanDefinition mbd,
2 @Nullable Object factoryBean, Method factoryMethod, Object[] args) {
3
4 try {
5 if (System.getSecurityManager() != null) {
6 return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
7 this.beanFactory.getInstantiationStrategy().instantiate(
8 mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
9 this.beanFactory.getAccessControlContext());
10 }
11 else {
12 return this.beanFactory.getInstantiationStrategy().instantiate(
13 mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
14 }
15 }
16 catch (Throwable ex) {
17 throw new BeanCreationException(mbd.getResourceDescription(), beanName,
18 "Bean instantiation via factory method failed", ex);
19 }
20 }

  其中factoryBean就是我们的当前第一个被解析到的配置类bean,截图为证,我自己写了两个配置类,第一个被加载的是MyASD,瞎写的名,好区分,第二个配置类是WebConfiguration,我们只看WebMvcConfigurationSupport里面的其中一个bean的创建过程,就是requestMappingHandlerAdapter,为啥要看这个,正好跟上节json自定义衔接。

https://www.cnblogs.com/guoxiaoyu/p/13667961.html

  到这里,我们可以看到在生成requestMappingHandlerAdapter时,调用extendMessageConverters方法时,一定会调用第一个配置类中的重写方法,因为所有的WebMvcConfigurationSupport里面 bean都被第一个配置类解析完了,所有的factoryBean都是当前第一个配置类,就算第二个配置完没有报错,也不会生效了。

  我直接把这个问题用源码的方式讲解清楚,方便大家明白为什么配置两个WebMvcConfigurationSupport类,只有一个生效。


项目里出现两个配置类继承WebMvcConfigurationSupport时,为什么只有一个会生效(源码分析)的更多相关文章

  1. 网狐6603 cocos2dx 棋牌、捕鱼、休闲类游戏《李逵捕鱼》手机端完整源码分析及分享

    该资源说明: cocos2d 棋牌.捕鱼.休闲类游戏<李逵捕鱼>手机端完整源码,网狐6603配套手机版源码,可以选桌子,适合新手学习参考,小编已亲测试,绝对完整可编译手机端,下载后将文件考 ...

  2. SpringBoot swagger-ui.html 配置类继承 WebMvcConfigurationSupport 类后 请求404

    1 .SpringBoot启动类加上  注解 @EnableWebMvc @SpringBootApplication@EnableWebMvc public class Application { ...

  3. SPringBoot 配置类继承WebMvcConfigurationSupport和实现WebMvcConfigurer的使用

    个人习惯使用  实现的方式 public class WebMvcConfiguration implements WebMvcConfigurer {

  4. 转:Ogre源码分析之Root类、Facade模式

    Ogre源码分析(一)Root类,Facade模式 Ogre中的Root对象是一个Ogre应用程序的主入口点.因为它是整个Ogre引擎的外观(Façade)类.通过Root对象来开启和停止Ogre是最 ...

  5. 【Spring源码分析系列】启动component-scan类扫描加载过程

    原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...

  6. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  7. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  8. Spring Boot 2.x 启动全过程源码分析(上)入口类剖析

    Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boot 为什么这么简便的奥秘. 本篇基于 Spring Boot 2.0.3 版本进 ...

  9. [Abp vNext 源码分析] - 11. 用户的自定义参数与配置

    一.简要说明 文章信息: 基于的 ABP vNext 版本:1.0.0 创作日期:2019 年 10 月 23 日晚 更新日期:暂无 ABP vNext 针对用户可编辑的配置,提供了单独的 Volo. ...

随机推荐

  1. 一文带你深扒ClassLoader内核,揭开它的神秘面纱!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! 前言 Clas ...

  2. 状态压缩动态规划(状压DP)详解

    0 引子 不要999,也不要888,只要288,只要288,状压DP带回家.你买不了上当,买不了欺骗.它可以当搜索,也可以卡常数,还可以装B,方式多样,随心搭配,自由多变,一定符合你的口味! 在计算机 ...

  3. JavaScript闭包(内存泄漏、溢出以及内存回收),超直白解析

    1 引言 变量作用域 首先我们先铺垫一个知识点--变量作用域: 变量根据作用域的不同分为两种:全局变量和局部变量. 函数内部可以使用全局变量. 函数外部不可以使用局部变量. 当函数执行完毕,本作用域内 ...

  4. e3mall商城的归纳总结10之freemarker的使用和sso单点登录系统的简介

    敬给读者的话 本节主要讲解freemarker的使用以及sso单点登录系统,两种技术都是比较先进的技术,freemarker是一个模板,主要生成一个静态静态,能更快的响应给用户,提高用户体验. 而ss ...

  5. 万字长文,Python数据分析实战,使用Pandas进行数据分析

    文章目录 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人,我给大家 ...

  6. 网络测速神器:SpeedTest深度指南

    最近在测试一个项目,里面涉及到一个测试case:在linux服务器上,当网络带宽较差时,观察服务的消息处理能力和表现.限制网卡带宽有许多方法,比如Wondershaper或者ethtool.那验证限速 ...

  7. leetcode刷题-59螺旋矩阵2

    题目 给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 思路 与螺旋矩阵题完全一致 实现 class Solution: def generateM ...

  8. 【极致丝滑】彻底摆脱编辑器插件,利用postcss灵活可控地转换px至vw

    背景 旧的rem适配方案(无论是直接使用rem,还是配合flexiblejs等lib库进行视口缩放)已经疲态尽显,且随着安卓高清屏的不断出现,同时data-dpr仍有进一步增加的可能性,rem显得并不 ...

  9. Spring框架学习笔记(1)

    Spring 框架学习笔记(1) 一.简介 Rod Johnson(spring之父) Spring是分层的Java SE/EE应用 full-stack(服务端的全栈)轻量级(跟EJB比)开源框架, ...

  10. odoo10中的邮件提醒

    odoo10中邮件提醒配置如下: 1.配置出向邮件服务器 打开开发者模式,设置-->技术-->email-->出向邮件服务器 设置如下: 如果配置成功,点击’测试连接‘,会出现如下弹 ...