【学习笔记】springboot的AutoConfigurationImportSelector 、@EnableAutoConfiguraion和@import解析

2023-11-05

@EnableAutoConfiguration介绍

自动装配在Spring Boot中是通过@EnableAutoConfiguration注解来开启的,这个注解的声明在启动类注解@SpringBootApplication内。 进入@SpringBootApplication注解,可以看到@EnableAutoConfiguration注解的声明。

其实Spring 3.1版本就已经支持@Enable注解了,它的主要作用把相关组件的Bean装配到IoC容器中。
**@Enable注解对JavaConfig的进一步完善,为使用Spring Framework的开发者减少了配置代码量,降低了使用的难度。**比如常见的@Enable注解有@EnableWebMvc、@EnableScheduling等。

如果基于JavaConfig的形式来完成Bean的装载,则必须要使用@Configuration注解及@Bean注解。而@Enable本质上就是针对这两个注解的封装,所以大家如果仔细关注过这些注解,就不难发现这些注解中都会携带一个@Import注解,比如@EnableScheduling:因此,使用@Enable注解后,Spring会解析到@Import导入的配置类,从而根据这个配置类中的描述来实现Bean的装配,那么@EnableAutoConfiguraion实现原理是不是也一样呢?

进入@EnableAutoConfiguration注解里,可以看到除@Import注解之外,还多了一个@AutoConfigurationPackage注解(它的作用是把使用了该注解的类所在的包及子包下所有组件扫描到Spring IoC容器中)。并且,**@Import注解中导入的并不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector类。**从这一点来看,它就和其他的@Enable注解有很大的不同。


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

AutoConfigurationImportSelector

AutoConfigurationImportSelector源码:

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
	private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
	private static final String[] NO_IMPORTS = {};
	private static final Log logger = LogFactory
			.getLog(AutoConfigurationImportSelector.class);

	private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

	private ConfigurableListableBeanFactory beanFactory;

	private Environment environment;

	private ClassLoader beanClassLoader;

	private ResourceLoader resourceLoader;
	....
	}

它实现了DeferredImportSelector,接口,而DeferredImportSelector又继承了ImportSelector,

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

它只有一个selectImports抽象方法,并且返回一个String数组,在这个数组中可以指定需要装配到IoC容器的类,当在@Import中导入一个ImportSelector的实现类之后,会把该实现类中返回的Class名称都装载到IoC容器中。 和@Configuration不同的是,ImportSelector可以实现批量装配,并且还可以通过逻辑处理来实现Bean的选择性装配,也就是可以根据上下文来决定哪些类能够被IoC容器初始化。
其中DeferredImportSelector有一个方法:

	@Nullable
	default Class<? extends Group> getImportGroup() {
		return null;
	}

其执行逻辑是,当实现DeferredImportSelector类重写了

public Class<? extends Group> getImportGroup()
方法时,则在此类被@Import注解导入时执行getImportGroup方法,如果没有实现getImportGroup方法,则执行

public String[] selectImports(AnnotationMetadata importingClassMetadata)

例子:使用importSelector

1.先创建两个类

public class FirstClass {
}
public class SecondClass {
}



2.我们需要把这两个类装配到IoC容器中。 ·
创建一个ImportSelector的实现类,在实现类中把定义的两个Bean加入String数组,这意味着这两个Bean会装配到IoC容器中。 ·

/**
 * @Author
 * @Description importselector实现类,用于把自定义的类加入bean数组
 * @Date
 **/
public class GpImportSelector implements ImportSelector {
    @Override
    public String [] selectImports(AnnotationMetadata metadata)
    {
        return new String[]{FirstClass.class.getName(),SecondClass.class.getName()};
    }
}

3.为了模拟EnableAutoConfiguration,我们可以自定义一个类似的注解,通过@Import导入GpImportSelector。


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(GpImportSelector.class)
public @interface MyEnableleImport {
}

4.· 创建一个启动类,在启动类上使用@EnableAutoImport注解后,即可通过getBean从IoC容器中得到FirstClass对象实例。

@MyEnableleImport
@SpringBootApplication

public class HwMusicApplication {

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext ca = SpringApplication.run(HwMusicApplication.class, args);
        FirstClass firstClass = ca.getBean(FirstClass.class);
        firstClass.get();//自己随意写的一个方法

    }

}

这种实现方式相比@Import(*Configuration.class)的好处在于装配的灵活性,还可以实现批量。比如GpImportSelector还可以直接在String数组中定义多个Configuration类,由于一个配置类代表的是某一个技术组件中批量的Bean声明,所以在自动装配这个过程中只需要扫描到指定路径下对应的配置类即可。

自动装配原理分析

自动装配的核心是扫描约定目录下的文件进行解析,解析完成之后把得到的Configuration配置类通过ImportSelector进行导入,从而完成Bean的自动装配过程。那么接下来我们通过分析AutoConfigurationImportSelector的实现来求证这个猜想是否正确。
定位到AutoConfigurationImportSelector中的selectImports方法,它是ImportSelector接口的实现,这个方法中主要有两个功能:

· AutoConfigurationMetadataLoader.loadMetadata

从META-INF/spring-autoconfigure-metadata.properties中加载自动装配的条件元数据,简单来说就是只有满足条件的Bean才能够进行装配。

· 收集所有符合条件的配置类
autoConfigurationEntry.getConfigurations(),完成自动装配。

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

该类的其他方法:
· getAttributes获得@EnableAutoConfiguration注解中的属性exclude、excludeName等。 ·
getCandidateConfigurations获得所有自动装配的配置类

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

· removeDuplicates去除重复的配置项。

· getExclusions根据@EnableAutoConfiguration注解中配置的exclude等属性,把不需要自动装配的配置类移除。 ·

fireAutoConfigurationImportEvents广播事件。

· 最后返回经过多层判断和过滤之后的配置类集合。

总的来说,它先获得所有的配置类,通过去重、exclude排除等操作,得到最终需要实现自动装配的配置类。这里需要重点关注的是getCandidateConfigurations,它是获得配置类最核心的方法。 这里用到了SpringFactoriesLoader,它是Spring内部提供的一种约定俗成的加载方式,类似于Java中的SPI。简单来说,它会扫描classpath下的META-INF/spring.factories文件,spring.factories文件中的数据以Key=Value形式存储,而SpringFactoriesLoader.loadFactoryNames会根据Key得到对应的value值。因此,在这个场景中,Key对应为EnableAutoConfiguration,Value是多个配置类,也就是getCandidateConfigurations方法所返回的值。

总结

▶▶▶▶@EnableAutoConfiguraion上有一个@import注解,@Import注解中导入的并不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector类,AutoConfigurationImportSelectori 实现了DeferredImportSelector, DeferredImportSelector继承了importSelector接口,实现了importSelector接口 重写了selectImports的类可以批量装载bean到ioc容器中

▶▶▶▶· 通过Spring提供的SpringFactoriesLoader机制,扫描classpath路径下的META-INF/spring.factories,读取需要实现自动装配的配置类。 ·
▶▶▶▶通过条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。 (@Conditional条件装配)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【学习笔记】springboot的AutoConfigurationImportSelector 、@EnableAutoConfiguraion和@import解析 的相关文章

  • Java 集合的并集或交集

    建立并集或交集的最简单方法是什么Set在 Java 中 我见过这个简单问题的一些奇怪的解决方案 例如手动迭代这两个集合 最简单的单行解决方案是这样的 set1 addAll set2 Union set1 retainAll set2 In
  • 检测并缩短字符串中的所有网址

    假设我有一条字符串消息 您应该将 file zip 上传到http google com extremelylonglink zip http google com extremelylonglink zip not https stack
  • 无法创建请求的服务[org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]-MySQL

    我是 Hibernate 的新手 我目前正在使用 Spring boot 框架并尝试通过 hibernate 创建数据库表 我知道以前也问过同样的问题 但我似乎无法根据我的环境找出如何修复错误 休眠配置文件
  • 像 Java 这样的静态类型语言中动态方法解析背后的原因是什么

    我对 Java 中引用变量的动态 静态类型和动态方法解析的概念有点困惑 考虑 public class Types Override public boolean equals Object obj System out println i
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • tomcat 中受密码保护的应用程序

    我正在使用 JSP Servlet 开发一个Web应用程序 并且我使用了Tomcat 7 0 33 as a web container 所以我的要求是tomcat中的每个应用程序都会password像受保护的manager applica
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • 不接受任何内容也不返回任何内容的函数接口[重复]

    这个问题在这里已经有答案了 JDK中是否有一个标准的函数式接口 不接受也不返回任何内容 我找不到一个 像下面这样 FunctionalInterface interface Action void execute 可运行怎么样 Functi
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 专门针对 JSP 的测试驱动开发

    在理解 TDD 到底是什么之前 我就已经开始编写测试驱动的代码了 在没有实现的情况下调用函数和类可以帮助我以更快 更有效的方式理解和构建我的应用程序 所以我非常习惯编写代码 gt 编译它 gt 看到它失败 gt 通过构建其实现来修复它的过程
  • 非 Spring 托管类中 DI 的编译时编织

    我想为标记为的类配置编译时编织 Configurable注释能够将 spring 依赖项注入到初始化的类中new操作员 我不想使用加载时编织 因为我无权访问应用程序服务器的运行脚本 因此无法修改它 另外 我希望能够在测试中使用此类 我的意思
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • 包 javax.el 不存在

    我正在使用 jre6 eclipse 并导入 javax el 错误 包 javax el 不存在 javac 导入 javax el 过来 这不应该是java的一部分吗 谁能告诉我为什么会这样 谢谢 米 EL 统一表达语言 是 Java
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 在java中为组合框分配键

    我想添加一个JComboBox在 Swing 中这很简单 但我想为组合中的每个项目分配值 我有以下代码 JComboBox jc1 new JComboBox jc1 addItem a jc1 addItem b jc1 addItem
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • 使用 CXF-RS 组件时,为什么我们使用 而不是普通的

    作为后续这个问题 https stackoverflow com questions 20598199 对于如何正确使用CXF RS组件我还是有点困惑 我很困惑为什么我们需要
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐

  • go语言基础-----20-----TCP网络编程

    1 网络编程介绍 目前主流服务器一般均采用的都是 Non Block I O多路复用 有的也结合了多线程 多进程 不过I O多路复用也给使用者带来了不小的复杂度 以至于后续出现了许多高性能的I O多路复用框架 比如libevent libe
  • No Feign Client for loadBalancing defined.错误

    SpringCloud OpenFeign报错 No Feign Client for loadBalancing defined Did you forget to include spring cloud starter loadbal
  • 《黑马程序员MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化》学习笔记总目录

    本文是对 黑马程序员MySQL数据库入门到精通 从mysql安装到mysql高级 mysql优化 所有知识点的笔记进行总结分类 学习视频 黑马程序员MySQL 学习时总结的目录笔记以及思维导图和实训 可通过点击以下链接 并输入提取码 M2S
  • 可重入锁的概念及使用场景

    在java中我们听过或者用过的锁有很多种 公平锁 非公平锁 可重入锁 不可重入锁 共享锁 排他锁 乐观锁 悲观锁 偏向锁 轻量级锁 重量级锁 其实这些都是在不同维度或者锁优化角度对锁的一种叫法 我们在程序中用到的也就那么几种 比如synch
  • js定时器单次执行、循环执行

    1 定时器定义 定时器 用以指定在一段特定的时间后执行某段程序 2 定时器应用 设置只执行一次的定时器 window setTimeout send 1000 设置重复执行的定时器 self setInterval send 8 1000
  • 新手购买了服务器怎么进入

    服务器一般是远程控制进行操作使用 通过服务器远程软件填写服务器IP 端口 用户名及其密码或授权文件进行访问 还可分为桌面图形版和命令行窗口版 对于Linux服务器专业人员来说 使用命令行窗口版较多 基本上的控制是使用命令行操作即可 当然啦
  • linux下宽字符文件, Linux上 wfopen(打开宽字符版的文件名和模式)的实现 (**)

    目录 linux下宽字符文件 Linux上wfopen 打开宽字符版的文件名和模式 的实现 https blog csdn net ken2232 article details 130316198 QString toWCharArray
  • 数字基带信号(主要涉及基带编码、传输系统)

    一 数字基带信号 1 数字基带信号 所谓数字基带信号 就是消息代码的电波形 数字基带信号的类型很多 本节以由矩形脉冲构成的基带信号为例 主要研究这些基带信号的时域波形 频谱波形以及功率谱密度波形 remark 信息是非实体 信源的信息必须外
  • 神经网络学习小记录53——TF2搭建孪生神经网络(Siamese network)比较图片相似性

    神经网络学习小记录53 TF2搭建孪生神经网络 Siamese network 比较图片相似性 学习前言 什么是孪生神经网络 代码下载 孪生神经网络的实现思路 一 预测部分 1 主干网络介绍 2 比较网络 二 训练部分 1 数据集的格式 2
  • vue3中界面使用router,以及使用watch来监听router的改变

    前言 众所周知 vue2中使用router非常简单 但是vue3中略微有些改变 这里来罗列下他的改变 1 路由跳转 vue2 this router push vue3 import useRouter from vue router co
  • cv2.error: OpenCV(4.7.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:971:...

    这个错误表明在使用 OpenCV 库的 highgui 模块时 程序传入的图像宽度为0 导致断言失败 这通常是由于读取的图像不存在或者文件路径有误导致的 建议检查图像路径是否正确 并确保图像文件存在
  • List集合添加指定元素到指定位置

    以下内容来自 JDK API 1 6 版本 List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法 方法摘要 boolean add E e 向列表的尾部添加指定的元素 可选操作 void add int index E el
  • 工业以太网通讯Profinet协议详解

    Profinet是通过西门子控制系统被广泛使用的工业通信协议 是一种较新的 基于以太网的工业通讯协议 Profinet使用的物理接口是一个标准的RJ 45以太网插口 Profinet电缆如下图 通过它的绿色外皮很好辨认 虽然在某些情况下 可
  • Lego_Loam--源码分析

    0 整体框架分析 翻看 LEGO Loam 的代码目录 首先进入到launch 文件中 看到
  • Spring boot Mybatis type-aliases-package错误解决

    背景 最近在练习spring boot 2 7 0整合mybatis 2 1 3时 发现在使用mybatis type aliases package配置后 xml中的别名会出现爆红的现象 错误复现 配置文件中 使用mybatis type
  • 开关电源基本原理和种类-反激-正激

    不可不知的几种开关电源及工作原理 前面分享了部分开关电源的基础知识 里面经常涉及不同种类的开关电源 虽然说 开关电源再怎么变 原理都一样 但过程细节总有区别 比如说 石墨和钻石都是同一种元素 碳 但性质有天地之别 扯远了 这次 我总结归纳了
  • **全排列实现数字1-9排序**

    在为蓝桥杯比赛备考过程中 真正体验到自己编程能力的薄弱 在一次小练习中接触全排列这一算法 基于对全排列的熟悉掌握 通过C语言代码实现数字1 9的全排列 当然也可以进行全排列的拓展 C语言实现数字1 9全排列 include
  • 计算机组成原理笔记03

    计算机组成原理笔记03 做题笔记1 内容 教材的思维导图 课后练习 计算部分 中国大学MOOC计算机组成原理 计算部分 1 教材的思维导图 在看题之前 最好先看这篇定点运算 写的特别清晰明了 2 课后练习 3 2 选择题 1 一个C语言程序
  • vue.config.js

    use strict const path require path function resolve dir return path join dirname dir const CompressionPlugin require com
  • 【学习笔记】springboot的AutoConfigurationImportSelector 、@EnableAutoConfiguraion和@import解析

    文章目录 EnableAutoConfiguration介绍 AutoConfigurationImportSelector 例子 使用importSelector 自动装配原理分析 总结 EnableAutoConfiguration介绍