十四、SpringBoot-自动装配原理

news2024/11/18 7:39:07

十四、SpringBoot-自动装配原理

SpringBoot与Spring比较起来,优化的点主要有:

  • 自动配置:是一个运行时(应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个,此过程是SpringBoot自动完成
  • 起步依赖:是一个Maven项目对象模型,定义了对其他库的传递依赖,这些东西加在一起支持某项功能,即将具有某种功能的坐标打包到一起,提供一些默认的功能
  • 辅助功能:提供一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标、健康检测等

SpringBoot不能直接从jar包或者其他项目中获取Bean,可以通过以下方式获取

  • 1、使用ComponentScan扫描
  • 2、使用@Import注解加载
  • 3、对Import注解进行封装

SpringBoot项目当我们想使用某个非自定义Bean时,通常只需要@Bean就能获取,不用再特殊引入依赖或者指定Bean,这就是用到了SpringBoot的自动配置,它的的实现就用到了几个重要的注解:

  • Enable系列注解:动态开启某些功能,底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载
  • @Import:注解导入类,会加载到SpringIOC容器中
  • Condition系列注解:条件判断,可以实现选择性的创建Bean操作

Enable系列注解:

假设现在项目A引用了依赖B,需要获取通过自定义@Enable获取到依赖B中的Bean信息

依赖B中的Bean信息

有一个为User的类

public class User {
    private String name;
}

对应的有一个UserConfig配置类

@Configuration
public class UserConfig {

    @Bean
    public User user(){
        return new User();
    }

}

这时候再自定义一个 @EnableUser 注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}

这时候就可以将依赖B的坐标导入到项目A中,在项目A中获取此User的B

@SpringBootApplication
@EnableUser
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

@Import注解

使用@Import注解导入类,会加载到SpringIOC容器中,有4种导入方式:

  • 1、导入Bean
  • 2、导入配置类
  • 3、导入ImportSelector实现类,一般用于加载配置文件中的类
  • 4、导入ImportBeanDefinitionRegistrar实现类

有一个为User的类

public class User {
    private String name;
}

导入Bean

直接使用@Import(类名.class)导入

@SpringBootApplication
@Import(User.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

导入配置类

首先创建配置类,对应的有一个UserConfig配置类

@Configuration
public class UserConfig {

    @Bean
    public User user(){
        return new User();
    }

}

使用@Import(配置类类名.class)导入

@SpringBootApplication
@Import(UserConfig.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

导入ImportSelector实现类,一般用于加载配置文件中的类

实现ImportSelector接口,实现selectImports(AnnotationMetadata annotationMetadata)方法

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.myinport.domain.User"};
    }
}

使用@Import(ImportSelector实现类类名.class)导入

@SpringBootApplication
@Import(MyImportSelector.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

导入ImportBeanDefinitionRegistrar实现类

实现ImportBeanDefinitionRegistrar接口

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("user",beanDefinition);
    }
}

使用@Import(ImportBeanDefinitionRegistrar实现类类名.class)导入

@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

Condition,条件判断

@Conditional注解内判断Condition接口实现类的matches返回值是否为true,还有@ConditionalOnProperty判断属性是否正确、@ConditionalOnClass判断class是否存在、@ConditionalOnMissingBean判断是否存在自定义同名Bean注解,

下面以三种例子来判断要不要加载User的Bean信息

User类

public class User {
    private String name;
}

@Conditional用法示例:

实现Condition接口,返回是否加载

public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    	return true;
    }
}

对应有一个配置类

@Configuration
public class UserConfig {

    @Bean
    @Conditional(ClassCondition.class)
    public User user() {
        return new User();
    }
}

这时候因为返回值恒为true,则能获取到Bean信息

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {

        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);

    }

}

@ConditionOnClass示例:

配置类信息,判断若存在redis.clients.jedis.Jedis的Bean信息,则加载User的Bean信息

@Configuration
public class UserConfig {

    @Bean
    @ConditionOnClass({"redis.clients.jedis.Jedis"})
    public User user() {
        return new User();
    }

}

当然也可以用实现Condition接口做到,返回是否加载

public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        boolean flag = true;
        try {
            Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            flag = false;
        }

        return flag;

    }
}

ConditionalOnProperty示例:

对应的配置类

@Configuration
public class UserConfig {

    @Bean
    @ConditionalOnProperty(name = "myKey",havingValue = "myValue")
    public User user() {
        return new User();
    }
}

对应的配置文件

myKey=myValue

这样也能判断是否加载User的Bean信息


自动装配源码

从SpringBoot项目的启动类配置进入 @SpringBootApplication注解

在这里插入图片描述
@SpringBootApplication注解内部含有 @EnableAutoConfiguration注解

在这里插入图片描述
@EnableAutoConfiguration注解内部Import了AutoConfigurationImportSelector类

在这里插入图片描述
观察类中的方法执行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后此方法就会去查找引用的起步依赖jar包中的 META-INF/spring.factories 中指定的加载信息,以spring-boot-autoconfigure包为例

在这里插入图片描述
在这里插入图片描述
它就会通过Conditional系列的注解去判断,此Bean信息是否应该加载。

整体流程就是:@SpringBootApplication -> @EnableAutoConfiguration -> AutoConfigurationImportSelector -> selectImports() -> SpringFactoriesLoader.loadFactoryNames() -> META-INF/spring.factories,再根据Condition进行判断是否加载进SpringIOC中

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试基础丨测试工程师之间要善于发现闪光点——测试理念篇

测试理念有多种&#xff0c;有一些理念&#xff0c;深藏于我的心中&#xff0c; 而这些理念&#xff0c;您或许偶尔想到&#xff0c;却没有说出&#xff0c;或许您感受到了&#xff0c;却因为工作生活的忙碌&#xff0c;没有将其背后的含义想具体&#xff0c; 在此我非常愿意和…

零基础小白hadoop分布式集群环境搭建(超详细)

搭建集群所需要安装包 虚拟机、ubuntu镜像文件、jdk安装包、hadoop安装包 百度云盘地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ejVamlrlyoWtJRo1QQqlsA提取码&#xff1a;fcqm 本文的环境是两台windows笔记本&#xff0c;在每台笔记本上安装一个虚拟机&…

超详细的水果FL Studio21最新版更新全功能详细介绍!80项更新与改进!

万众期待的 FL Studio 21 版本将于正式发布上线&#xff0c;目前在紧锣密鼓的安排上线中&#xff0c;届时所有购买正版 FL Studio 的用户&#xff0c;都可以免费升级到21版&#xff01;按照惯例&#xff0c;本次新版也会增加全新插件&#xff0c;来帮助大家更好地创作。今天先给…

SMART原则介绍

一、SMART原则简介 什么是SMART原则? SMART原则(S=Specific、M=Measurable、A=Attainable、R=Relevant、T=Time-bound)是为了利于员工更加明确高效地工作,更是为了管理者将来对员工实施绩效考核提供了考核目标和考核标准,使考核更加科学化、规范化,更能保证考核的公正、…

五万字详解“GoF”的23种设计模式

大家好&#xff0c;我是栗筝i&#xff0c;近期我总结梳理了 “GoF”的 23 种设计模式&#xff0c;并使用 Java 对每种设计模式都进行了伪代码与 Demo 实现&#xff0c;并总结了每种设计模式的应用场景&#xff0c;优缺点&#xff0c;UML图等相关内容&#xff0c;字/词数达到了5…

Java中的String

/*** 关于java.lang.String类* 1、String表示字符串类型&#xff0c;属于引用数据类型&#xff0c;不属于基本数据类型* 2、在java中用双引号括起来的都是String对象* 3、java中规定&#xff0c;字符串是不可变的* 4、字符串存储在方法区的字符串常量池当中*/ …

单例模式(python)

一、模式定义 1. 单例模式(Singleton Pattern)&#xff1a;确保某一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供这个实例&#xff0c;这个类称为单例类&#xff0c;它提供全局访问的方法。 2. 单例模式的要点有三个&#xff1a; 某个类只能有一个实例 必须自…

[激光原理与应用-41]:《光电检测技术-8》- 白光干涉仪

目录 第1章 白光干涉仪概述 第2章 常见干涉仪 2.1 激光量块干涉仪 2.2 白光干涉测量表面形貌的系统 第1章 白光干涉仪概述 用于光在两个不同表面反射后形成的干涉条纹进行分析的设备。 干涉仪是一种对光在两个不同表面反射后形成的干涉条纹进行分析的仪器。 其基本原理就…

SpringColud——Ribbon(负载均衡)Hystrix(熔断器)

目录 1、Ribbon 1.1、什么是Ribbon&#xff08;负载均衡&#xff09; 1.2、创建两个user-service实例 1.3、开启负载均衡 2、Histrix&#xff08;熔断器&#xff09; 2.1、什么是Histrix 2.2、雪崩问题 2.3、服务降级 2.4、开启熔断 2.5、编写降级逻辑 2.6、编写降级…

顶象App加固——助力微投证券融入IOS生态

过去十年里&#xff0c;App几乎重新定义了互联网&#xff0c;如今所有人的智能手机里都有着无数个App。 以App store 为例。最新数据显示&#xff0c;当前61个国家/地区在 App Store 现存的App总数为29,085,727。其中虽包含重复的App&#xff0c;但也可见在不同国家/地区&…

毕业设计 基于STM32单片机的老人防摔倒报警系统 - 物联网 嵌入式

文章目录0 前言1 整体设计2 硬件电路3 软件设计4 跌倒检测算法5 关键代码6 最后0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉…

Partition of an interval

In mathematics, a partition of an interval [a, b] on the real line is a finite sequence x0, x1, x2, …, xn of real numbers such that a x0 < x1 < x2 < … < xn b. In other terms, a partition of a compact interval I is a strictly increasing seq…

《小白WEB安全入门》01. 扫盲篇

扫盲篇基础知识什么是WEB什么是前端什么是后端什么是数据库什么是协议什么是WEB安全什么是服务器什么是IP地址、端口什么是局域网、广域网、内网、外网什么是URL什么是MAC地址什么是&#xff08;端口&#xff09;映射什么是域名、DNS什么是网卡、网关什么是IPv4/IPv6什么是Linu…

51单片机的温控风扇设计

一.硬件方案 系统采用51单片机作为控制平台对风扇转速进行控制。可由用户设置高、低温度值&#xff0c;测得温度值在高低温度之间时打开风扇弱风档&#xff0c;当温度升高超过所设定的温度时自动切换到大风档&#xff0c;当温度小于所设定的温度时自动关闭风扇。风扇控制状态随…

论坛系统设计与实现

摘 要 网络技术的快速发展给各行各业带来了很大的突破&#xff0c;也给各行各业提供了一种新的管理模块&#xff0c;对于论坛交流将是又一个传统管理到智能化信息管理的改革&#xff0c;设计论坛系统设计与实现的目的就是借助计算机让复杂的论坛交流操作变简单&#xff0c;变…

第8部分 帧中继

帧中继线路是中小企业常用的广域网线路&#xff0c;其通信费用较低。由于帧中继技术的一些特殊性使得帧中继的配置较为复杂&#xff0c;特别是在帧中继上运行路由协议时更是如此。作为入门&#xff0c;对帧中继的理解应着重放在DLCI、PVC、帧中继映射和子接口等概念上。本章通过…

Redis--高级篇 D5 多级缓存(JVM进程缓存、Lua语法、OpenResty安装(通过lua扩展nginx))

1、JVM进程缓存 1.1 导入商品查询案例 案例导入说明 为了演示多级缓存&#xff0c;我们先导入一个商品管理的案例&#xff0c;其中包含商品的CRUD功能。我们将来会给查询商品添加多级缓存。 1.安装MySQL 后期做数据同步需要用到MySQL的主从功能&#xff0c;所以需要大家在虚…

【Pytorch】.item() 方法介绍

文章目录一、.item() 方法介绍1. 方法介绍2. 那么有什么用呢&#xff1f;二、实例参考链接一、.item() 方法介绍 1. 方法介绍 我们先看官网中是怎么介绍的&#xff1a; 返回这个张量的值作为一个标准的Python数字。 详情页中说&#xff1a; # TORCH.TENSOR.ITEM Tensor.ite…

配对交易之统计套利配对:模型的应用

接下来&#xff0c;介绍把协整模型应用到取对数之后的股价上面。 前提假设&#xff1a;股价取对数之后是一个随机游走过程&#xff0c;即非平稳的。 现在有股票A和B,股价取对数之后的两个时间序列是以及。使用误差修正的表达方式&#xff1a; 能够唯一确定以上式子的参数是协…

uView教程-抽屉菜单 #低代码 #小程序 #uView

这种抽屉效果是如何制作的呢&#xff1f; 在guiplan低代码开发工具中&#xff0c; 点击"uView框架", 输入关键字"弹出层"进行搜索&#xff0c; 找到"带用户菜单" 点击"一键插入", 这我们一个抽屉菜单就插入进来了&#xff0c; 底部…