【Spring Boot】原理

本文先通过介绍Bean的不同加载方式引出Spring Boot中的自动装配原理。文中的图片内容参考了黑马程序员在B站上的内容。

Bean的加载方式

1. XML+ < bean/ >

2.XML: context + 注解(@Component)

3.配置类+扫描+注解(@Component)

  • @Bean定义FactoryBean接口
  • @ImportResource
  • @Configuration注解的proxyBeanMethods属性

4.@Import导入Bean的类

  • @Import导入配置类

5.AnnotationConfigApplicationContext调用register方法

6.@Import导入ImportSelector接口

7.@Import导入ImportBeanDifinitionRegistrar接口

8.@Import导入BeanDefinitionRegistryPostProcessor接口

最后对容器中的Bean进行干预,其能影响最终结果。

Bean的加载控制

  • Bean的加载控制根据特定情况对Bean进行选择性加载。

Bean的依赖属性配置

通过读取properties来读取属性。

@EnableConfigurationProperties(**.class)

这个注解可以将读取数据的类加载为Bean也就是需要在对应的类上加@Component注解。

自动配置原理

1.收集Spring开发者的编程习惯,整理开发过程使用的常用技术列表(技术集A)

2.收集常用技术的使用参数(技术集A),整理开发过程中每一个技术的常用设置列表(设置集B)

3.初始化SpringBoot基础环境,加载用户自定义的Bean和导入其他坐标,形成初始环境。

4.将技术集A包含的所有技术都定义出来,在Spring启动时默认全部加载

5.将技术集A中具有使用条件的技术约定出来,设置成按照条件加载,由开发者决定是否使用该技术(与初始环境对比)

6.将设置集B作为默认配置加载(约定大于配置),减少开发者配置工作量。

7.开放设置集B的配置覆盖接口,由开发者根据自身需要度额定是否覆盖默认配置。

自动配置原理-源码

首先从@SpringBootApplication注解开始,这是一个组合注解,其中包含了很多注解。

主要的注解包括了下面三个:

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

第一个:@SpringBootConfiguration

@Configuration

@Component

@Indexed:主要在SpringBoot启动期加速。

第二个:@EnableAutoConfiguration

@AutoConfigurationPackage

重点 @Import(AutoConfigurationPackages.Registrar.class)
重点@Import(AutoConfigurationImportSelector.class)

AutoConfigurationPackages.Registrar 实现了 ImportBeanDefinitionRegistrar

这一段的功能主要是 生成了一个名为 包路径 的Bean

java 复制代码
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			//扫描当前包的路径
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}

AutoConfigurationImportSelector实现了很多接口,主要分为三类

第一类是 DeferredImportSelector 继承自 ImportSelector

第二类是 注入资源 Aware相关 BeanClassLoaderAware,

ResourceLoaderAware, BeanFactoryAware, EnvironmentAware

通过实现Aware接口可以方便的实现对于拿取容器中的资源。

第三类是 Ordered 当前类在Spring容器中的加载顺序。

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered 

第一个类 DeferredImportSelector 继承了 ImportSelector 并且推迟加载

ImportSelector中的需要实现如下抽象方法:

java 复制代码
	String[] selectImports(AnnotationMetadata importingClassMetadata);

Deferred中还定义了一个Group的接口:其中的核心是process方法

java 复制代码
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
			AutoConfigurationImportSelector autoConfigurationImportSelector = (AutoConfigurationImportSelector) deferredImportSelector;
			AutoConfigurationReplacements autoConfigurationReplacements = autoConfigurationImportSelector
				.getAutoConfigurationReplacements();
			Assert.state(
					this.autoConfigurationReplacements == null
							|| this.autoConfigurationReplacements.equals(autoConfigurationReplacements),
					"Auto-configuration replacements must be the same for each call to process");
			this.autoConfigurationReplacements = autoConfigurationReplacements;
			AutoConfigurationEntry autoConfigurationEntry = autoConfigurationImportSelector
				.getAutoConfigurationEntry(annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

上方代码中:

java 复制代码
			AutoConfigurationEntry autoConfigurationEntry = autoConfigurationImportSelector.getAutoConfigurationEntry(annotationMetadata);
java 复制代码
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// 获取元注解的的所有属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 元注解:exclude 和 excludeName : 在技术集A中不需要的配置在这里;也就是获取技术集A中排除掉exclude中的其他配置作为候选①
		List<String> configurations = getCandidateConfigurations(annotationMextadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

①中的getCandidateConfigurations方法的实现如下:

java 复制代码
	public static ImportCandidates load(Class<?> annotation, @Nullable ClassLoader classLoader) {
		Assert.notNull(annotation, "'annotation' must not be null");
		ClassLoader classLoaderToUse = decideClassloader(classLoader);
		String location = String.format(LOCATION, annotation.getName());
		Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
		List<String> importCandidates = new ArrayList<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			importCandidates.addAll(readCandidateConfigurations(url));
		}
		return new ImportCandidates(importCandidates);

在这里需要注意:低版本的Spring Boot在自动加载时的机制是扫描FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" 下的所有包。

但是在Spring Boot 3.x及之后的版本中已经移除,修改为LOCATION = "META-INF/spring/%s.imports"

在程序运行后 %s 会被替换,

默认路径为:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

在文件夹中可以看到其默认配置:

这里 相比于之前使用的spring.factories 少了很多个autoConfiguration

java 复制代码
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration

当更新坐标之后:假设添加Redis的坐标:

xml 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

添加后:

其中的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 就会被添加候选配置中:

但是在这里也需要注意:有以下的几个注解,@ConditionalOnClass中对应的类存在才会加载对应的技术集到自动配置当中,

java 复制代码
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(DataRedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public final class DataRedisAutoConfiguration {
...
}

如何变更自动配置

老版本变更自动配置:

新版本变更自动配置:

需要在对应的 META-INF/spring文件夹下新建一个名称为:

org.springframework.boot.autoconfigure.AutoConfiguration.imports 的文件,在文件中标明需要自动装配的类即可。

比如这样:edu.neusoft.learn.bean.CartoonCatAndMouse

其中还涉及到一些注解的解释:

@ConfigurationProperties(prefix = "cartoon") 这个注解配置在属性类上,其中的前缀对应的在配置文件中的前缀,然后之后就按照属性名进行配置即可。

@EnableConfigurationProperties(CartoonProperties.class)这个注解是配置在需要将当前的属性注入到当前的类的单例对象当中。

如果不走自动配置,也就是如果不在imports文件中手写对应类名,就需要使用@Import(CartoonCatAndMouse.class)手动导入。

@ConditionalOnClass(Cat.class)这个注解一般加在需要自动装配的类上,用以当前装配的条件,只有某个对应的类文件存在,那么才自动配置当前的bean。

相关推荐
一只叫煤球的猫1 小时前
用这个框架彻底摆脱Controller,从此专注业务——ArcRoute
java·spring·开源
SunnyDays10111 小时前
Java 如何根据模板高效生成Word文档
java·根据模板生成word文档·生成word文档
攀岩巨峰的程序猿1 小时前
代码开发过程中涉及到bean的copy方法梳理
java
golang学习记1 小时前
IDEA 2026.1 EAP 5 发布:K2模式更强了!
java·ide·intellij-idea
xuansec2 小时前
【JavaEE安全】Java反序列化深度剖析:核心原理、利用链构造与安全风险管控
java·安全·java-ee
艾莉丝努力练剑2 小时前
静态地址重定位与动态地址重定位:Linux操作系统的视角
java·linux·运维·服务器·c语言·开发语言·c++
菜鸟小九2 小时前
hot100(31-40)
java·算法
xu_ws2 小时前
Spring-ai项目-deepseek-会话日志
java·人工智能·spring
江湖十年2 小时前
MCP 官方 Go SDK v1.0.0 正式发布:Go 生态的模型上下文协议步入稳定时代
人工智能·后端·go