【原理篇】三、SpringBoot自动配置原理

文章目录

  • 0、背景demo
  • 1、自动配置思路
  • 2、META-INF/spring.factories
  • 3、Redis自动配置
  • 4、自定义一个自动配置
  • 5、排除SpringBoot内置自动配置类的加载
  • 6、补充点:ApplicationContextAware接口

0、背景demo

用一个循序渐进的示例来体验属性配置,方便后面理解自动配置,先准备几个demo类:

@Data
public class Cat{

	private String name;
	
	private Integer age;
}
@Data
public class Mouse{

	private String name;
	
	private Integer age;
}
//猫和老鼠卡通类
@Component
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	//提供个构造方法给两个属性赋值,不然默认null,下面会空指针
	public CartoonCatAndMouse(){
		cat = new Cat();
		cat.setName("tom");
		cat.setAge(3);
		mouse= new Mouse();
		mouse.setName("jerry");
		mouse.setAge(3);
	}

	public void play(){
		System.out.println(cat.getAge()+"岁的"+cat.getName()+"与"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");
	}
}
//启动类中调用下play方法
@SpringBootApplication
public class App{

	public static void main(String[] args){
		
		ConfigurableApplicationContext ctx = SringApplication.run(app.class);
		
		CartoonCatAndMouse bean = ctx.getBean(CartoonCatAndMouse.class);
		
		bean.play();
	}
}

在这里插入图片描述

此时,配置都硬编码了,显然不合理。

优化第一步:引入yaml配置并@ConfigurationProperties读取

cartoon:
  cat:
    name: tom
    age: 3
  mouse:
    name: jerry
    age: 4
@Component
@ConfigurationProperties(prefix = "cartoon")
//加个set,不然cat属性和mouse属性为null
@Data  
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	public void play(){
		System.out.println(cat.getAge()+"岁的"+cat.getName()+"与"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");
	}
}

此时,如果yaml配置中没有相关配置,则对应的对象为null,进而空指针。也就是说,一加@ConfigurationProperties(prefix = “cartoon”),我的类和yaml配置绑死了,没配置,类都受影响。

优化第二步:引入独立配置类CartoonProperties

@ConfigurationProperties(prefix = "cartoon")
@Data
//旧知识点,读取yaml的类的对象必须受Spring容器管控,否则,即使拿到yaml值也无法set给你
@Component
public class CartoonProperties {
	private Cat cat;
	private Mouse mouse;
}

@Component
@Data  
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;
	
	//构造器注入了,用@Autowired也行
	private CartoonProperties cartoonProperties;

	public void play(CartoonProperties cartoonProperties){
		
		this.cartoonProperties = cartoonProperties;
		cat = new Cat();
		cat.setName(cartoonProperties.getCat()!=null && StringUtils.hasText(cartoonProperties.getCat().getName())
					?cartoonProperties.getCat().getName():"tom");  //有则用,无则用默认值
		//Mouse对象同样写法,略
		System.out.println(cat.getAge()+"岁的"+cat.getName()+"与"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");
	}
}

此时,有个缺点,CartoonProperties类不管用不用,都被强制加载成一个Bean了,但去掉@Component,只留@ConfigurationProperties语法错误 ⇒ 用@EnableConfigurationProperties

优化第三步:@EnableConfigurationProperties,改掉强制加载Bean

//@Component 不再需要定义为Bean
@ConfigurationProperties(prefix = "cartoon")
@Data
public class CartoonProperties {
	private Cat cat;
	private Mouse mouse;
}

@ConfigurationProperties和@EnableConfigurationProperties,前者是做属性绑定的,后者是开启属性绑定,并设定对应的目标是谁

@Component  //这个Component也可以去掉,用的时候@Import(CartoonCatAndMouse.class)
@Data  
//即当我加载CartoonCatAndMouse时,就用CartoonProperties.class,并把CartoonProperties类加载成Bean
@EnableConfigurationPropertiesCartoonProperties.class)   
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	//重复代码,略....

这套模式的亮点有以下几个:

  • 合理的加载配置文件,即你配置了就用你的,没配就用默认值来工作
  • 对于属性类xxxProperties不用强制配置成Bean,使用@EnableConfigurationProperties
  • 对于业务功能的Bean,通常使用@Import将一个类加载成Bean,来解耦强制加载Bean,以降低Spring容器管理Bean的工作量以及强度

1、自动配置思路

  • 收集Spring开发者的编程习惯,得到常用技术集列表 ⇒ 技术集A
  • 收集每个技术的常用参数值,得到常用配置值列表 ⇒ 设置集B
  • 初始化你项目的SpringBoot基础环境,包括加载用户自定义的Bean以及用户导入的其他坐标,得到初始化环境
  • 把技术集A中具有使用条件的技术设置为按条件加载,与初始化环境对比,如有Redis核心类时,就触发加载Redis技术集的资源
  • 约定大于配置,设置集B里的参数值做为默认配置加载
  • 对于要修改的配置,开发者自行覆盖

总之就是,SpringBoot官方整理了常用的技术以及对应的配置值,可根据你项目的环境来自动加载相关的Bean,并给你默认配置,减少开发者的工作量。

2、META-INF/spring.factories

从启动类的@SpringBootApplication注解开始看:

在这里插入图片描述
关键点:

  • @Import(AutoConfigurationPackages.Registrar.class) ,debug可发现,这里是设置当前配置所在的包为扫描包,后续要针对当前包进行扫描
  • @Import(AutoConfigurationImportSelector.class)

继续往下看AutoConfigurationImportSelector,它实现了很多接口,可分三类:

  • DeferredImportSelector文末补充点一节
  • Ordered:有关Bean的创建顺序

重写的DeferredImportSelector接口中的process方法中除去断言代码,往下debug看getAutoConfigurationEntry方法:

在这里插入图片描述

调用了获取候选配置的方法getCandidateConfigurations:

在这里插入图片描述

跳过断言代码,看到核心肯定在loadFactoryNames方法:

在这里插入图片描述
继续debug方法loadFactoryNames,除去判空,关键的在loadSpringFactories方法:

在这里插入图片描述

可以看到loadSpringFactories方法在读META-INF/spring.factories里的资源,这里就是核心了。

在这里插入图片描述

3、Redis自动配置

查看SpringBootAutoConfiguration下的spring.factories文件:

在这里插入图片描述

以Redis的自动装配为例来看:spring.factories文件钟找到了RedisAutoConfiguration类,即Redis的自动配置类:

在这里插入图片描述

对比前面刚开始的背景案例,这个Redis的自动装配就很明晰了:

  • @ConditionOnClass是Redis相关Bean加载的条件,引入Redis起步依赖后,里面包含这里的RedisOperations.class类,也就满足了加载条件
  • @EnableConfigurationProperties后面是属性配置类
  • @Import导入两个Redis底层的Bean
  • 定义两个Redis客户端操作的Template的Bean,并前提是用户没有自定义这个Bean

这就是前面提到的,SpringBoot加了无数技术的自动配置类,用对应的条件来检测你当前Spring项目中要不要加对应的技术的Bean等资源。

4、自定义一个自动配置

对比上面的Redis自动配置类和开篇的demo,前面的CartoonCatAndMouse类就是RedisAutoConfiguration,CartoonProperties就是RedisProperties,那就模仿官方写法,把CartoonCatAndMouse改成一个真正的自动配置类。新建META-INF/spring.factories,内容:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.plat.bean.CartoonCatAndMouse

此时,CartooCatAndMouse就完成了自动配置,修一下,加上加载条件:

@Data 
@ConditionalOnClass(name = "com.plat.core.MyCore.class")
@EnableConfigurationPropertiesCartoonProperties.class)   
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	//重复代码,略....

此时,项目启动,有MyCore.class类被加载时,触发自动装配,完成相关Bean的加载。最后,如果直接把一个类配置到spring.factories文件中,能从IoC容器中get到这个Bean吗? ==> 可以,但属性都为null

5、排除SpringBoot内置自动配置类的加载

截至SpringBoot2.6版本,spring.factories中已有130多种技术对应的自动配置类,没必要启动时去全部判断一次,可以直接排除掉一些你肯定不用的自动配置类。

  • 方法一:配置文件
spring:
  autoconfigure:
    exclude: 
      - org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
      - org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
  • 方法二:注解
@SpringBootApplication(excludeName = "",exclude = {})

//exclude属性其实是@EnableAutoConfiguration注解的,但它被@SpringBootApplication包含,属性也被拿了过来
  • 方式三:排除掉依赖,直接通过干涉激活条件@Conditional实现,如去除tomcat自动配置(条件激活),添加jetty自动配置(条件激活)

在这里插入图片描述

6、补充点:ApplicationContextAware接口

获取ApplicationContext对象,可以通过实现ApplicationContextAware接口:

@Component
public class MyIocUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext){
        this.applicationContext = applicationContext;    //给当前工具类的applicationContext属性赋值
    }
 
    public static ApplicationContext getApplicationContext(){
        return applicationContext;
    }
}

此时,在你需要使用上下文对象的地方直接:

MyIocUtils.getApplicationContext()

即可拿到上下文对象。当然你不封装成工具类也行,直接在你需要用上下文对象的类里写:

public class YourClass{

	private ApplicationContext applicationContext;
	
	@Override
    public void setApplicationContext(ApplicationContext applicationContext){
        this.applicationContext = applicationContext;    //给当前类的applicationContext属性赋值
    }

	public void doSome(){
		//使用上下文对象
		applicationContext.getBean........
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/124032.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Oracle 安装及 Spring 使用 Oracle

参考内容: docker安装oracle数据库史上最全步骤(带图文) Mac下oracle数据库客户端 Docker安装Oracle docker能安装oracle吗 Batch script for add a auto-increased primary key for exist table with records Docker 安装 Oracle11g 注意&a…

用Java(C语言也可以看)实现冒泡排序和折半查找(详细过程图)+逆序数组

目录 一、冒泡排序 1.冒泡排序介绍 2.排序的思路 3.完整代码 二、折半查找 1.折半查找介绍 2.查找的思路 3.完整代码 三、逆序数组 1.逆序思路 2..完整代码 一、冒泡排序 冒泡排序是众多排序的一种,无论在C语言或者Java中都很常见,后续在数据…

浅谈智能变电站自动化系统的应用与产品选型

安科瑞电气股份有限公司 上海嘉定 201801 摘要:现如今,智能变电站发展已经成为了电力系统发展过程中的内容,如何提高智能变电站的运行效率也成为电力系统发展的一个重要目标,为了能够更好地促进电力系统安全稳定运行,…

轻量封装WebGPU渲染系统示例<22>- 渲染到纹理(RTT)(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/RTTTest.ts 当前示例运行效果: 此示例基于此渲染系统实现,当前示例TypeScript源码如下: export class RTTTest {private mRscene new RendererScene()…

使用 Threejs 从基础开始构建 3D 地球

需求 threejs学习-3D 地球 实现: 1、使用粒子效果模拟宇宙星空 2、贴图、模型等资源的加载 3、加载资源的监听 4、效果合成器 EffectComposer 的初级使用 5、在地球上设置坐标以及坐标涟漪动画 6、标点间建立飞线 7、简单动画建议先浏览一遍git地址上代码&#xff…

SpringMVC简介

SpringMVC简介 一、MVC是什么二、什么是SpringMVC?1.特点 三、简单实现 一、MVC是什么 MVC是模型视图控制器的简称,是指一种架构思想。 M:Model,模型层,指工程中的JavaBean,作用是处理数据。 JavaBean分为…

A Survey on Neural Network Interpretability

A Survey on Neural Network Interpretability----《神经网络可解释性调查》 摘要 随着深度神经网络的巨大成功,人们也越来越担心它们的黑盒性质。可解释性问题影响了人们对深度学习系统的信任。它还与许多伦理问题有关,例如算法歧视。此外,…

基于Python的书籍数据采集与可视化分析系统

温馨提示:文末有 CSDN 平台官方提供的学长 Wechat / QQ 名片 :) 1. 项目简介 基于Python的书籍数据采集与可视化分析系统旨在挖掘和分析海量图书数据背后的规律和趋势,为读者、出版商和数据分析师提供更深入的洞察和辅助决策。本系统依托于某瓣庞大的图书…

JS逆向爬虫---请求参数加密②【某麦数据analysis参数加密】

主页链接: https://www.qimai.cn/rank analysis逆向 完整参数生成代码如下&#xff1a; const {JSDOM} require(jsdom) const dom new JSDOM(<!DOCTYPE html><p>hello</p>) window dom.windowfunction customDecrypt(n, t) {t t || generateKey(); //…

20 VPN详解

1、连接数据中心 1.第一种方式是走公网&#xff0c;但是公网太不安全 2.第二种方式是专线&#xff0c;但是比较贵 3.VPN连接2、vpn的概念 全名Virtual Private Network&#xff0c;虚拟专用网&#xff0c;就是利用开放的公众网络&#xff0c;建立专用数据传输通道&#xff0c…

洛谷P5731 【深基5.习6】蛇形方阵java版题解

import java.util.Arrays; import java.util.Scanner;// 给出一个不大于9的正整数n&#xff0c;输出nn的蛇形方阵。 public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int[][] a new int[n][n];int total…

《016.SpringBoot+vue校园社团管理系统》【有文档】

《016.SpringBootvue校园社团管理系统》【有文档】 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMyBatisPlus; 前台&#xff1a;vueElementUI; [2]功能模块展示&#xff1a…

HCIP---VLAN

文章目录 目录 目录 文章目录 前言 一.VLAN概述 作用&#xff1a; 二.VLAN基础认识 VLAN ID: VLAN端口类别 总结 前言 VLAN技术是现代企业网络和数据中心网络的核心技术之一。本文将重点针对VLAN技术进行详解。 一.VLAN概述 VLAN&#xff08;Virtual Local Area Network…

自动化实战 - 测试个人博客系统

前言 本篇使用Selenium3Junit5对个人博客进行自动化测试&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言一.web自动化测试用例二.测试准备1.注册界面自动化测试测试过程中遇到的Bug: 2.登录界面自动…

Java基础——数组(一维数组与二维数组)

文章目录 一维数组声明初始化与赋值内存图解 二维数组声明初始化与赋值内存图解 数组练习杨辉三角冒泡排序线性查找二分法数组反转 数组是多个相同类型的数据按一定顺序排列的集合。 说明&#xff1a; 数组是引用数据类型&#xff0c;数组的元素是同一类型的任何数据类型&…

【C#学习笔记】事件

前言 在之前我学习委托的时候&#xff0c;写到了 学习了委托&#xff0c;事件其实也就学习了&#xff0c;事件和委托基本上一模一样&#xff1a; 然而在实际工作中通过对事件的深入学习后发现&#xff0c;实际上事件的使用比委托要严格一些&#xff0c;本节将详细讲解事件的使…

介绍两个好用又好玩的大模型工具

先让数字人跟大家打个招呼吧。 我的AI数字人会手语了 发现没&#xff0c;我的数字人本周又学了一个新技能&#xff1a;手语。 这些数字人都是通过AI生成的。 但数字人不是今天的主题&#xff0c;今天要跟大家聊聊大模型。 自从大模型出现后&#xff0c;很多人&#xff08;包…

20行JS代码实现屏幕录制

在开发中可能有遇到过屏幕录制的需求&#xff0c;无论是教学、演示还是游戏录制&#xff0c;都需要通过屏幕录制来记录和分享内容。一般在App内H5页基于客户端能力实现的较多&#xff0c;现在浏览器中的 MediaRecorder 也提供了这种能力。MediaRecorder 是一种强大的技术&#…

Mybatis(一)

1. Mybatis简介 MyBatis下载地址 1.1 MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c;iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github…

【遍历二叉树的非递归算法,二叉树的层次遍历】

文章目录 遍历二叉树的非递归算法二叉树的层次遍历 遍历二叉树的非递归算法 先序遍历序列建立二叉树的二叉链表 中序遍历非递归算法 二叉树中序遍历的非递归算法的关键&#xff1a;在中序遍历过某个结点的整个左子树后&#xff0c;如何找到该结点的根以及右子树。 基本思想&a…