SpringBoot自动装配原理详解

首先对于一个SpringBoot工程来说,最明显的标志的就是 @SpringBootApplication它标记了这是一个SpringBoot工程,所以今天的 SpringBoot自动装配原理也就是从它开始说起。

自动装配流程

首先我们来看下@SpringBootApplication 这个注解的背后又有什么玄机呢,我们按下 ctrl + 鼠标左键,轻轻的点一下,此时见证奇迹的时刻..
我们看到如下优雅的代码:

这其中有两个比较容易引起我们注意的地方,一个是@SpringBootConfiguration注解,另一个是@EnableAutoConfiguration注解;之所以说这个两个注解比较吸引我们的眼球, 不是因为它们长大的好看,而是因为其他的注解太难看了(主要是因为其他的注解我们都是比较熟悉,即使不知道他们是干什么的,可以肯定更自动装配是没有关系的)。 然后我们又伸出了邪恶的小手,开启了熟悉的操作,按下了Ctrt + 鼠标左键,瞪着色咪咪的小眼睛,瞳孔放大了百倍等待着奇迹的出现... 擦... 擦...擦...

什么也没有...
那我要你有何用,这么顶级的世界级的开源项目,怎么会让一个没用的家伙存在呢? 于是动用了上亿的脑细胞大军,经过复杂的运算,得出了一个不靠谱的结论:它可能使用来标记这是一个SpringBoot工程的配置。因为SpringBootConfiguration翻译过来就是SpringBoot的配置,于是心中又是几万只羊驼在万马奔腾,大漠飞扬。

气定神闲之后,秉承着·失败是成功之母"的信念, 熟练的左手行云流水般的按下了 Ctrl + Table 键,回到了最初的的地方。眼睛盯着 @EnableAutoConfiguration ,环顾左右,在地址栏输入了谷歌翻译, 结果显示 自动装配。我找的就是你,真是众里寻他千百度,那人却在灯火阑珊处。 熟练的按下了 Ctrl +左键,迫不及待的想要进入; 心里默默背诵起了《桃花源记》的经典诗句 ∶

林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗

此时此刻心情愉悦,有过前面的经历之后,在面对新的世界时候,我们淡定了许多。 此时大脑高速运转,没有再纠结,直捣黄龙,进入了 AutoConfigurationImportSelector.class 类,因为谷歌翻译告诉我们,这个是自动配置导入选择器。 于是我们发现了—片新天地

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
    // 获取自动配置的实体
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

  // 具体用来加载自动配置类得方法
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取候选的配置类,即使后宫佳丽三千,也是要筛选的
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 根据情况,自动配置需要的配置类和不需要的配置了
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, );
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
    // 返回最终需要的配置
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}

而这个自动配置的实体 AutoConfigurationEntry里面有两个属性,configurationsexclusions

	protected static class AutoConfigurationEntry {
    // 用来存储需要的配置项
		private final List<String> configurations;
    // 用来存储排除的配置项
		private final Set<String> exclusions;

		private AutoConfigurationEntry() {
			this.configurations = Collections.emptyList();
			this.exclusions = Collections.emptySet();
		}
  }

在后面可以看到 getAutoConfigurationEntry()方法返回了一个对象 return new AutoConfigurationEntry(configurations, exclusions);这里也就是把我们需要的配置都拿到了。

那他是怎么拿到的候选的配置类呢? 我们接着看这个获取候选配置类的方法 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
进到方法后我们看到下面这个方法具体获取候选配置类的方法内容

这里我们跟着断点去走,首先进入getSpringFactoriesLoaderFactoryClass()方法

	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    // 返回的是EnableAutoConfiguration字节码对象
		return EnableAutoConfiguration.class;
	}

接着我们在进入getBeanClassLoader()方法,这里就是一个类加载器

protected ClassLoader getBeanClassLoader() {
		return this.beanClassLoader;
	}

最后我们在进入loadFactoryNames()方法,这个方法就是根据刚才的字节码文件和类加载器来找到候选的配置类。传递过来的字节码

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
    // 获取的EnableAutoConfiguration.class的权限定名
    //org.springframework.boot.autoconfigure.EnableAutoConfiguration
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

如下图:

最后通过loadSpringFactories()来获取到所有的配置类

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    // 缓存加载的配置类
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
      // 去资源目录下找
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
      // 加载完成放到缓存中
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
    // 返回加载到的配置类
		return result;
	}

这里我们要看下怎么从资源目录下 FACTORIES_RESOURCE_LOCATION 加载的。下面是加载配置文件的路径:

也就是项目启动的时候会去加载所有 META-INF 下的所有的 spring.factories 文件,我们搜一下这个这个文件,我搭建的是一个很简单的 SpringBoot 工程,它会去这几个 jar 里面找相关的配置类

但是最后自动装配的类是这个spring-boot-autoconfigure-2.4.3.RELEASE.jar

而根据EnabLeAutoConfiguration.class字节码加载的配置类就只有这118自动配置类

小结

实际上SpringBoot的自动装配原理,其实就是在项目启动的时候去加载META-INF下的 spring.factories 文件,好像也没有那么高大上。当然在启动的过程中还会有其他的配置项的加载,这里咱么直说了自动装配的加载过程。希望对大家可以有所启发。

以上就是SpringBoot自动装配原理详解的详细内容,更多关于SpringBoot自动装配原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot启动及自动装配原理过程详解

    一.servlet2(老spring-mvc) 配置文件: web.xml:主要配置项目启动项 application-context.xml:主要配置项目包扫描.各种bean.事务管理 springMVC.xml:主要配置controller包扫描.视图解析器.参数解析器 启动过程: 每一个spring项目启动时都需要初始化spring-context,对于非web项目可以在程序main方法中触发这个context的初始化过程. 由于web项目的启动入口在容器,所以开发者不能直接触发sprin

  • Springboot自动装配实现过程代码实例

    创建一个简单的项目: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/PO

  • springboot如何实现自动装配源码解读

    Spring Boot 自动装配 最重要的注解@SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, c

  • 深入浅析SpringBoot中的自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉. 一.自动装配过程分析 1.1.关于@SpringBootApplication 我们在编写SpringBoot项目时,@SpringBootApplication是最常见的注解了,我们可以看一下源代码: /* * Copyright 2012-2017 the original author or authors. * * Licensed un

  • SpringBoot自动装配原理详解

    首先对于一个SpringBoot工程来说,最明显的标志的就是 @SpringBootApplication它标记了这是一个SpringBoot工程,所以今天的 SpringBoot自动装配原理也就是从它开始说起. 自动装配流程 首先我们来看下@SpringBootApplication 这个注解的背后又有什么玄机呢,我们按下 ctrl + 鼠标左键,轻轻的点一下,此时见证奇迹的时刻.. 我们看到如下优雅的代码: 这其中有两个比较容易引起我们注意的地方,一个是@SpringBootConfigur

  • Java SpringBoot自动装配原理详解及源码注释

    目录 一.pom.xml文件 1.父依赖 2.启动器: 二.主程序: 剖析源码注解: 三.结论: 一.pom.xml文件 1.父依赖 主要是依赖一个父项目,管理项目的资源过滤以及插件! 资源过滤已经配置好了,无需再自己配置 在pom.xml中有个父依赖:spring-boot-dependencies是SpringBoot的版本控制中心! 因为有这些版本仓库,我们在写或者引入一些springboot依赖的时候,不需要指定版本! 2.启动器: 启动器也就是Springboot的启动场景; 比如sp

  • Java Springboot自动装配原理详解

    目录 Debug路线图 让我们从run说起 归属 小结 run 再说说注解 总结 Debug路线图 说多都是泪,大家看图. 让我们从run说起 用了这么多年的的Springboot,这个 run() 方法到底做了些什么事呢? @SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] args) { SpringApplication.run(Springboot

  • SpringBoot自动配置原理详解

    目录 阅读收获 一.SpringBoot是什么 二.SpringBoot的特点 三.启动类 3.1 @SpringBootApplication 四.@EnableAutoConfiguration 4.1 @AutoConfigurationPackage 4.2  @Import({AutoConfigurationImportSelector.class}) 五.流程总结图 六.常用的Conditional注解 七.@Import支持导入的三种方式 阅读收获 理解SpringBoot自动配

  • SpringBoot自动装配原理小结

    约定优于配置(Convention Over Configuration)是一种软件设计范式,目的在于减少配置的数量或者降低理解难度,从而提升开发效率. 先总结一下结论: springboot通过spring.factories能把main方法所在类路径以外的bean自动加载,其目的就是为了帮助自动配置bean,减轻配置量 springboot autoconfig的一些实验 一个springboot工程,springbootautoconfig.test.config这个包和启动类的包不再同一

  • 浅谈springboot自动装配原理

    一.SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFi

  • springboot自动装配原理初识

    运行原理 为了研究,我们正常从父项目的pom.xml开始进行研究. pom.xml 父依赖 spring-boot-starter-parent主要用来管理项目的资源过滤和插件 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE<

  • 深入了解Java SpringBoot自动装配原理

    目录 自动装配原理 SpringBootApplication EnableAutoConfiguration AutoConfigurationImportSelector 总结 在使用springboot时,很多配置我们都没有做,都是springboot在帮我们完成,这很大一部分归功于springboot自动装配,那springboot的自动装配的原理是怎么实现的呢? 自动装配原理 springboot 版本:2.4.3 SpringBootApplication springboot启动类

  • Spring注解实现Bean自动装配示例详解

    何为自动装配 自动装配是 Spring 满足 bean 依赖的一种方式. 在使用 Spring 配置 bean 时,我们都要给配置的 bean 的属性设置一个值,如果不手动设置则都是空.而自动的好处就在于,我们不用手动去设置一个值,spring 会在上下文中自动寻找并装配合适的值. 本文主要介绍了Spring注解Bean自动装配的相关内容,下面话不多少了,来一起看看详细的介绍吧 使用须知: 1.导入约束:context约束 2.配置注解的支持: context:annotation-config

  • 最新springboot中必须要了解的自动装配原理

    目录 1.pom.xml 2.启动器 3.主程序 3.1注解 3.2 spring.factories 4. 结论 1.pom.xml 父 依 赖 \textcolor{orange}{父依赖} 父依赖 spring-boot-dependencies:核心依赖都在父工程中 这里ctrl+左键,点击之后我们可以看到父依赖 这个里面主要是管理项目的资源过滤及插件,我们发现他还有一个父依赖 看看下面这个,熟悉吗? 再点进去,我们发现有很多的依赖.这就是SpringBoot的版本控制中心. 这个地方才

随机推荐