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实现相关算法。 我们关注的主要对象是重新排列数组元素的算法,其中每个元素…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...