IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot的自动装配 -> 正文阅读

[Java知识库]SpringBoot的自动装配

一. 什么是SpringBoot自动装配

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

通俗来说,在我们没有使用SpringBoot之前,如果我们需要引入一个三方依赖或者组件,需要进行很多的配置才能实现,但是在使用了SpringBoot之后,我们只需要引入一个maven依赖即可完成,比如引入Redis到我们的项目中,就只需要加入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后进行一下Redis相关的地址配置即可,这种就是SpringBoot的自动装配机制

我们本篇分析的是自动装配的过程,所以重点只看自动装配相关的代码,关于过程中其他的SpringBoot启动的相关流程,后续会有文章进行分析。

二. 自动装配如何实现的

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Test1Application {

    public static void main(String[] args) {
       SpringApplication.run(Test1Application.class, args);
    }

}

一个普通的SpringBoot启动类,我们来看他的注解@SpringBootApplication
在这里插入图片描述

可以看到@SpringBootApplication实际上是一个组合注解,去除掉前四个元注解,剩下的

  • @SpringBootConfiguration 其实就是一个@Configuration注解
  • @EnableAutoConfiguration 这个注解是自动装配的关键,其中有两个@Import注解引入了自动装配需要的类:@Import(AutoConfigurationImportSelector.class)@Import(AutoConfigurationPackages.Registrar.class)
  • @ComponentScan 定义了包扫描的信息

这三个注解中,@EnableAutoConfiguration 表示开启自动装配,并引入了AutoConfigurationImportSelector这个自动装配的关键类。接下来我们从SpringApplication.run开始顺序分析自动装配的过程

三. 自动装配过程解析

从启动类的SpringApplication.run进入开始分析,回来到这一步:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return new SpringApplication(primarySources).run(args);
}

这个方法分两步,一个是new SpringApplication一个是调用new出来对象实例的run方法,我们先看new SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

这段代码是创建SpringApplication的构造方法,我们重点了来看其中的getSpringFactoriesInstances(ApplicationContextInitializer.class)这一步。至于后面的getSpringFactoriesInstances(ApplicationListener.class)和前面的一样流程,只不过是从缓存中取筛选数据进行实例化,所以就不再进行第二次分析了。
代码跟进会来到:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

在上面这个方法中,我们首先要清楚的是入参typeApplicationContextInitializer.class,然后重点来看SpringFactoriesLoader.loadFactoryNames(type, classLoader)

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

这个方法也是分两个步骤,一个是loadSpringFactories(classLoader)一个是根据入参的类型名称从中获取需要的内容,其实就是map.getOrDefault方法。我们重点跟进loadSpringFactories(classLoader)

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	// 当SpringBoot启动并第一次调用该方法时,这个cache肯定是空的,所以获取不到内容
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		// 通过classLoader加载META-INF/spring.factories资源  
		// 其实就是SpringBoot项目下的所有META-INF/spring.factories文件内容
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		// 创建一个多value的map 
		result = new LinkedMultiValueMap<>();
		// 循环加载到的资源并解析
		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();
				for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

上面这个方法其实算是自动装配中的第一个比较重要的步骤,加载所有的META-INF/spring.factories内容,这里贴出来一个

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-25 23:07:16  更:2022-09-25 23:07:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:19:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码