04-详解SpringBoot自动装配的原理,依赖属性配置的实现,源码分析
自动装配原理
依赖属性配置
提供Bean用来封装配置文件中对应属性的值
@Data
public class Cat {private String name;private Integer age;
}
@Data
public class Mouse {private String name;private Integer age;
}
cartoon:cat:name: "图多盖洛"age: 5mouse:name: "泰菲"age: 1
读取yml文件中的数据,将业务功能Bean运行需要的数据抽取出来封装到CartoonProperties对象中
- 缺点: 要想封装yml文件中的数据这个Bean必须由Spring管控,但其实如果我们没有导入业务功能Bean就没必要读取yml文件中的数据
Component
@ConfigurationProperties(prefix = "cartoon")
@Data // 需要给Cat和Mouse提供对应的getter和setter方法,才能把yml文件中的数据注入到Cat和Mouse对象中
public class CartoonProperties { private Cat cat;private Mouse mouse;
}
在业务Bean中根据需要读取CartoonProperties对象中的数据,@EnableConfigurationProperties开启属性类的配置绑定功能并强制把其注册到容器中
- 如果开发者在yml文件中配置了对应的属性的值就使用配置的值,如果没有配置就使用默认值
@Data
@EnableConfigurationProperties(CartoonProperties.class)
public class CartoonCatAndMouse{private Cat cat;private Mouse mouse;private CartoonProperties cartoonProperties;// 自动注入,如果有参的构造方法只有一个@Autowired注解可以省略public CartoonCatAndMouse(CartoonProperties cartoonProperties){this.cartoonProperties = cartoonProperties;cat = new Cat();// 如果开发者在yml文件中配置了对应的属性就使用配置的值,如果没有配置就使用默认值cat.setName(cartoonProperties.getCat()!=null && StringUtils.hasText(cartoonProperties.getCat().getName()) ? cartoonProperties.getCat().getName() : "tom");cat.setAge(cartoonProperties.getCat()!=null && cartoonProperties.getCat().getAge()!=null ? cartoonProperties.getCat().getAge() : 3);mouse = new Mouse();mouse.setName(cartoonProperties.getMouse()!=null && StringUtils.hasText(cartoonProperties.getMouse().getName()) ? cartoonProperties.getMouse().getName() : "jerry");mouse.setAge(cartoonProperties.getMouse()!=null && cartoonProperties.getMouse().getAge()!=null ? cartoonProperties.getMouse().getAge() : 4);}public void play(){System.out.println(cat.getAge()+"岁的"+cat.getName()+"和"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");}
}
使用@Import方式导入业务Bean,避免业务Bean强制加载,根据需要导入,降低Spring管控Bean的强度
- 缺点: 自动配置类我们也没必要强制加载成容器的Bean,应当是满足某种条件时才加载
@SpringBootApplication
@Import(CartoonCatAndMouse.class)
public class App {public static void main(String[] args) {ConfigurableApplicationContext ctx = SpringApplication.run(App.class);CartoonCatAndMouse bean = ctx.getBean(CartoonCatAndMouse.class);bean.play();System.out.println(ctx.getBean(Cat.class));}
}
自动配置源码分析
@SpringBootApplication底层相关的注解
@SpringBootApplication
public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class, args);}
}
| SpringBootApplication底层注解 | 注解的底层注解 |
|---|---|
| @SpringBootConfiguration | @Configuration,说明Spring Boot程序的启动类也是一个配置类 |
| @EnableAutoConfiguration | @AutoConfigurationPackage --> @ Import(AutoConfigurationPackages.Registrar.class) @Import(AutoConfigurationImportsSelector.class) |
| @ComponentScan | 指定扫描过滤的规则FilterType.CUSTOM和TypeExcludeFilter.class等, 默认扫描主程序所在包及其子包下的所有组件 |
@Import(AutoConfigurationPackages.Registrar.class)注解: 设置启动类的包作为基础扫描包, 后续将该包及其子包下注解标识类注册成Bean添加到容器中
AutoConfigurationPackages.Registrar是AutoConfigurationPackages(抽象类)的静态内部类
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {// 记录所有要扫描的包,启动类所在的包及其子包String[] basePackages() default {};Class<?>[] basePackageClasses() default {};static classimplementsImportBeanDefinitionRegistrar,DeterminableImportsRegistrar{@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// metadata是元数据,可以获取启动类上的所有注解信息// new PackageImports(metadata).getPackageNames()获取到的就是启动类所在的包register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}
}public static void register(BeanDefinitionRegistry registry, String... packageNames){// 判断容器中是否加载过AutoConfigurationPackagesif (registry.containsBeanDefinition(BEAN)){BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);beanDefinition.addBasePackages(packageNames);}else{// 注册一个叫com...AutoConfigurationPackages的Bean,将要扫描的包封装到BasePackagesBeanDefinition对象中registry.registerBeanDefinition(BEAN,new BasePackagesBeanDefinition(packageNames));}
}static final class BasePackagesBeanDefinition extends GenericBeanDefinition {private final Set<String> basePackages = new LinkedHashSet<>();BasePackagesBeanDefinition(String... basePackages) {setBeanClass(BasePackages.class);setRole(BeanDefinition.ROLE INFRASTRUCTURE);addBasePackages(basePackages);}
}
@Import(AutoConfigurationImportSelector.class): 指定工程启动时需要向容器中添加的所有自动配置类XxxAutoConfiguration
- 在
spring-boot-autoconfigure-xxx.jar包里面的META-INF/spring.factories目录下存放了工程启动时需要加载的所有类(含自动配置类)

// Spring中的Bean只要实现了XxxAware相关的接口并实现接口的setXxx方法,就可以在当前Bean中使用对应的对象
// Ordered表示加载的顺序,因为有些Bean加载的时候是要依赖其他Bean的,每个Bean都有对应的加载顺序
// DeferredImportSelector表示推迟的导入选择器
public class AutoConfigurationImportSelector implements DeferredImportSelector,BeanClassLoaderAware,ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered { private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}// 使用ApplicationContext接口中的相关方法String[] beans = applicationContext.getBeanDefinitionNames();for (String bean : beans) {System.out.println(bean);}
}@Override
// selectImports方法的返回值是一个String类型数组,数组的元素就是我们要批量导入的组件
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:} // 获取启动类上的所有注解及其属性,@EnableAutoConfiguration注解有exclude,excludeName两个属性可以按照Class对象和全类名排除不需要加载的类 AnnotationAttributes attributes = getAttributes(annotationMetadata);// 获取候选的配置,读取META-INF/spring.factories中的数据,将所有自动配置类的全类名添加到一个List集合中并返回List<String> configurations= getCandidateConfigurations(annotationMetadata, 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);
}// 调用loadFactoryNames方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesloader.loadFactoryNames(getSpringFactoriesloaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, message: "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;
}// 调用loadSpringFactories方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable Classloader classloader){// 获取类加载器ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == nul1) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}// 获取字符串String factoryTypeName = factoryType.getName();return loadSpringFactories(classloaderToUse).getorDefault(factoryTypeName, Collections,emptylist());
}// 最终调用的方法
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 {// 通过类加载器加载外部资源,spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面的META-INF/spring.factories目录下的自动配置类Enumeration<URL> urls = classLoader,getResources(FACTORIES_ESOURCE_OCATION);while (urls.hasMoreElements()){URL url= urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);}}
测试XxxAutoConfiguration
AopAutoConfiguration自动配置类的生效条件
@Configuration(proxyBeanMethods = false)
// 判断是否存在一个配置文件有spring.aop的前缀属性,默认是存在的
@ConditionalOnProperty(prefix = "spring.aop",name = "auto",havingValue = "true",matchIfMissing = true
)
public class AopAutoConfiguration {public AopAutoConfiguration() {}...
}
RedisAutoConfiguration自动配置类的生效条件及其绑定的属性配置类 RedisProperties
@Configuration(proxyBeanMethods = false)
// 要加载RedisAutoConfiguration必须有RedisOperations,这个类在spring-boot-starter-data-redis中
@ConditionaionClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({LettuceConnectionConfiguration,class, JedisConnectionConfiguration,class})
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate")@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public RedisTemplate<0bject, 0bject> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template}@Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}
}
RedisProperties封装了yml文件中的spring.redis属性的值
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {private int database = 0;private String url;private String host = "localhost";// .....
}
自动配置流程
第一步: 将开发过程使用的常用技术列表整理成一个技术集A即所有的自动配置类XxAutoConfiguration,工程启动时默认会全部加载到内存中
- 这些自动配置类虽然会全部加载到内存中,但不会全部生效, 只有满足实际条件的自动配置类及其内部的才会注册成容器中的Bean
- 每个自动配置类都对应一个属性配置类,用来封装配置文件中指定前缀的属性值,自动配置类需要用时会从
xxxProperties对象中获取
第二步: 将这些常用技术需要设置的参数整理成一个设置集B即所有的属性配置类xxxxProperties用来封装yml文件中对应属性的值
第三步: 开放设置集B的配置覆盖接口,若开发者在yml文件中配置了某属性的值,对应属性配置对象中的对应属性就可以获取到值,如果没有配置对应属性为默认值
- SpringBoot默认会在底层配好所有的组件, 但是如果用户自己配置了以用户的优先,如直接通过定义@Bean替换底层的组件或者去修改这个组件获取的配置文件值
第四步: 生效的自动配置类从对应属性配置对象中获取值然后为要创建的组件赋值(约定大于配置),若获取的属性值是null就使用默认值,如果不是就使用获取到的值
第五步: 加载用户自定义的Bean和导入的其他坐标,检测每个自动配置类的加载条件是否满足, 最后初始化SpringBoot的基础环境
变更自动配置
@SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration")// 排除加载的自动配置类
//@Import(CartoonCatAndMouse.class) // 根据条件装配这个自动配置类
public class App {public static void main(String[] args) {ConfigurableApplicationContext ctx = SpringApplication.run(App.class);CartoonCatAndMouse bean = ctx.getBean(CartoonCatAndMouse.class);bean.play();System.out.println(ctx.getBean(Cat.class));}
}
SpringBoot中自带的自动配置类有130个,后面的技术若想要实现自动配置功能需要手动在t工程中的resources/META-INF目录下添加spring.factories文件
- SpringBoot默认会扫描我们当前工程里面所有的
META-INF/factories文件(每个jar包都是一个工程)
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.itheima.bean.CartoonCatAndMouse
在配置文件中排除加载的自动配置类
spring:autoconfigure:exclude:- org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
相关文章:
04-详解SpringBoot自动装配的原理,依赖属性配置的实现,源码分析
自动装配原理 依赖属性配置 提供Bean用来封装配置文件中对应属性的值 Data public class Cat {private String name;private Integer age; }Data public class Mouse {private String name;private Integer age; }cartoon:cat:name: "图多盖洛"age: 5mouse:name: …...
[100天算法】-不同路径 III(day 73)
题目描述 在二维网格 grid 上,有 4 种类型的方格:1 表示起始方格。且只有一个起始方格。 2 表示结束方格,且只有一个结束方格。 0 表示我们可以走过的空方格。 -1 表示我们无法跨越的障碍。 返回在四个方向(上、下、左、右&#…...
【c++随笔12】继承
【c随笔12】继承 一、继承1、继承的概念2、3种继承方式3、父类和子类对象赋值转换4、继承中的作用域——隐藏5、继承与友元6、继承与静态成员 二、继承和子类默认成员函数1、子类构造函数 二、子类拷贝构造函数3、子类的赋值重载4、子类析构函数 三、单继承、多继承、菱形继承1…...
Excel中使用数据验证、OFFSET实现自动更新式下拉选项
在excel工作簿中,有两个Sheet工作表。 Sheet1: Sheet2(数据源表): 要实现Sheet1中的“班级”内容,从数据源Sheet2中获取并形成下拉选项,且Sheet2中“班级”内容更新后,Sheet1中“班…...
Android修行手册 - 可变参数中星号什么作用(冷知识)
点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资源分享&…...
Python与ArcGIS系列(三)视图缩放
目录 0 简述1 在所有图层中缩放至所选要素2 在单独图层中缩放至所选要素3 改变地图范围0 简述 本篇介绍如何利用arcpy实现缩放视图到所选要素以及改变地图范围功能。 对于以及创建的选择集数据,通常需要进行缩放以更好地显示所选要素,要素缩放可分为两种:第一种是在所有图层…...
[ASP]数据库编辑与管理V1.0
本地测试:需要运行 ASP专业调试工具(自己搜索下载) 默认登陆口令:admin 修改口令:打开index.asp找到第3行把admin"admin"改成其他,如admin"abc123" 程序功能齐全,代码精简…...
MyBatis Plus整合Redis实现分布式二级缓存
MyBatis缓存描述 MyBatis提供了两种级别的缓存, 分别时一级缓存和二级缓存。一级缓存是SqlSession级别的缓存,只在SqlSession对象内部存储缓存数据,如果SqlSession对象不一样就无法命中缓存,二级缓存是mapper级别的缓存ÿ…...
如何帮助 3D CAD 设计师实现远程办公
当 3D CAD 设计师需要远程办公时,他们可能需要更强的远程软件,以满足他们的专业需求。比如高清画质,以及支持设备重定向、多显示器支持等功能。3D CAD 设计师如何实现远程办公?接下来我们跟随 Platinum Tank Group 的故事来了解一…...
如何在 Idea 中修改文件的字符集(如:UTF-8)
以 IntelliJ IDEA 2023.2 (Ultimate Edition) 为例,如下: 点击左上角【IntelliJ IDEA】->【Settings…】,如下图: 从弹出页面的左侧导航中找到【Editor】->【File Encodings】,并将 Global Encoding、Project E…...
【C++】单例模式【两种实现方式】
目录 一、了解单例模式前的基础题 1、设计一个类,不能被拷贝 2、设计一个类,只能在堆上创建对象 3、设计一个类,只能在栈上创建对象 4、设计一个类,不能被继承 二、单例模式 1、单例模式的概念 2、单例模式的两种实现方式 …...
php的api接口token简单实现
<?php // 生成 Token function generateToken() {$token bin2hex(random_bytes(16)); // 使用随机字节生成 tokenreturn $token; } // 存储 Token(这里使用一个全局变量来模拟存储) $tokens []; // 验证 Token function validateToken($token) {gl…...
CCNA课程实验-13-PPPoE
目录 实验条件网络拓朴需求 配置实现基础配置模拟运营商ISP配置ISP的DNS配置出口路由器OR基础配置PC1基础配置 出口路由器OR配置PPPOE拨号创建NAT(PAT端口复用) PC1测试结果 实验条件 网络拓朴 需求 OR使用PPPoE的方式向ISP发送拨号的用户名和密码,用户名…...
cocosCreator 之 Bundle使用
版本: v3.4.0 语言: TypeScript 环境: Mac Bundle简介 全名 Asset Bundle(简称AB包),自cocosCreator v2.4开始支持,用于作为资源模块化工具。 允许开发者根据项目需求将贴图、脚本、场景等资源划分在 Bundle 中&am…...
分类网络搭建示例
搭建CNN网络 本章我们来学习一下如何搭建网络,初始化方法,模型的保存,预训练模型的加载方法。本专栏需要搭建的是对分类性能的测试,所以这里我们只以VGG为例。 请注意,这里定义的只是一个简陋的版本,后续一…...
为 Ubuntu 虚拟机构建 SSH 服务器
以校园网环境和VMware为例,关键步骤如下: 安装 SSH 服务: 打开 Ubuntu 虚拟机。打开终端。输入命令 sudo apt-get update 更新软件包列表。输入命令 sudo apt-get install openssh-server 安装 SSH 服务。 配置 SSH 服务: 编辑配…...
SpringBoot--中间件技术-2:整合redis,redis实战小案例,springboot cache,cache简化redis的实现,含代码
SpringBoot整合Redis 实现步骤 导pom文件坐标 <!--redis依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>yaml主配置文件,配置…...
linux rsyslog配置文件详解
1.rsyslog配置文件简介 linux rsyslog配置文件/etc/rsyslog.conf分为三部分:MODULES、GLOBAL DIRECTIVES、RULES ryslog模块说明 模块说明MODULES指定接收日志的协议和端口。若要配置日志服务器,则需要将相应的配置项注释去掉。GLOBAL DIRECTIVES主要用来配置日志模版。指定…...
wordpress是什么?快速搭网站经验分享
作者主页 📚lovewold少个r博客主页 ⚠️本文重点:c入门第一个程序和基本知识讲解 👉【C-C入门系列专栏】:博客文章专栏传送门 😄每日一言:宁静是一片强大而治愈的神奇海洋! 目录 前言 wordp…...
排序 算法(第4版)
本博客参考算法(第4版):算法(第4版) - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台 本文用Java实现相关算法。 我们关注的主要对象是重新排列数组元素的算法,其中每个元素…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
raid存储技术
1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划,涵盖存储系统的布局、数据存储策略等,它明确数据如何存储、管理与访问,为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...
英国云服务器上安装宝塔面板(BT Panel)
在英国云服务器上安装宝塔面板(BT Panel) 是完全可行的,尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎,虽然官方主要面向中国大陆…...
