【Spring Boot-SpringBoot怎么实现自动配置】

news2024/9/22 4:03:20

目录

什么是Spring Boot自动配置

自动配置中需要的重要注解

一.@Condition

二.@Enable

三.@EnableAutoConfiguration

实现一个自定义starter


什么是Spring Boot自动配置

        SpringBoot的自动配置简单来说就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们去手动装配,从而简化开发,省去了大部分繁琐的配置操作。

 

自动配置中需要的重要注解

        我们接下来通过一步一步了解Spring Boot自动配置中的关键注解来明白SpringBoot怎么实现自动配置。

一.@Condition

        @Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建Bean 操作。

        增加条件判断注解的目的是为了使我们的程序动态的开启自动配置。

举例说明:

        我们使用Spring Boot进行程序开发时,会需要在pom.xml配置文件中进行坐标导入,当你导入某个坐标后,你在程序中就能使用需要的类和方法。

这里以导入Redis坐标为例:

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

当你导入坐标之后,你的程序就装配了该依赖库封装的两个模板类,帮助我们实现操作redis。这时你在启动类中getBean("redisTemplate"),就能获取到其中redisTemplate模板类。

@SpringBootApplication
public class SpringbootCondition01Application {

    public static void main(String[] args) {

        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context =  SpringApplication.run(SpringbootCondition01Application.class, args);
        //获取Bean,redisTemplate
         Object redisTemplate = context.getBean("redisTemplate");
         System.out.println(redisTemplate);
    }
}

启动后获取到

如果你没有添加starter-data-redis坐标,那么从容器中获取模板类则报空

那么在以上例子中其实就实现了通过@Conditon进行选择配置,如果存在该坐标就帮你在容器中注入模板类。

为了更好的理解,我们可以自己使用@Condition来选择注入一下。

假设我们现在需求在 Spring 的 IOC 容器中有一个 User 的 Bean,要求:导入Jedis坐标后,加载该Bean,没导入,则不加载。

        1.创建一个User类

public class User {

}

因为我们不能直接在User类上直接写注入注解,这样就不能选择,直接注入了User类。所以创建一个间接创建User类的配置类。 

        2.创建UserConfig配置类

@Configuration  //声明配置类
public class UserConfig {

    @Bean //向容器中注入一个User类型的user对象
    @Conditional(value= ClassCondition.class) //判断,通过ClassCondition的实现类
    public User user(){
        return new User();
    }
}

       3.创建ClassCondition实现类

        必须实现Condition接口。

public class ClassCondition implements Condition {
    /**
     *
     * @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
     * @param metadata 注解元对象。 可以用于获取注解定义的属性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1.需求: 导入Jedis坐标后创建Bean
        //思路:判断redis.clients.jedis.Jedis.class文件是否存在

        boolean flag = true;
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;

    }
}

        4.测试

        在启动类,从容器中获取getBean("user")。

因为我们没有导入jedis坐标,所以报错。

 

二.@Enable

        SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。

例如@EnableAsync注解可以使Bean在spring应用中支持异步功能,@EnableWebMvc注解引入了MVC框架在Spring应用中需要用到的所有bean。

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


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

可以看到其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。 

@Import注解 :@Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。
@Import提供4种用法:
        ① 导入Bean
        ② 导入配置类
        ③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
        ④ 导入 ImportBeanDefinitionRegistrar 实现类。

举例说明:

 ① 导入Bean

        简单创建一个User类并且创建User的配置类,使用@Import将User类导入当前容器。这里还是选择在启动类中导入并且getBean()测试。

  ② 导入配置类

        其实就是①的基础上,可以导入User的配置类。也成功getBean

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

        创建一个实现ImportSelector接口的实现类,并且需要重写selectImports方法。

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //目前字符串数组的内容是写死的,未来可以设置在配置文件中动态加载
        return new String[]{"com.apesource.ttt.pojo.User"};
    }
}

测试成功

  ④ 导入 ImportBeanDefinitionRegistrar 实现类。

 创建实现ImportBeanDefinitionRegistrar 的实现类

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        
        //1.获取user的definition对象
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();

        //2.通过beanDefinition属性信息,向spring容器中注册id为user的对象
        registry.registerBeanDefinition("user", beanDefinition);

    }
}

测试成功 

 

三.@EnableAutoConfiguration

 我们观察启动类,可以发现启动类上都有@SpringBootApplication注解。

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//以为是启动了一个方法,没想到启动了一个服务
SpringApplication.run(SpringbootApplication.class, args);
}
}

进入 @SpringBootApplication注解内部

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ......
}

其中有
1.@ComponentScan
        这个注解在Spring中很重要 ,它对应XML配置中的元素。
        作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中           
          
2.@SpringBootConfiguration
        作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类; 

//@SpringBootConfiguration注解内部
//这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
@Configuration
public @interface SpringBootConfiguration {}
//里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
@Component
public @interface Configuration {}

3.@EnableAutoConfiguration开启自动配置功能

        作用:@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

        在@EnableAutoConfiguration注解内部又包含一些重要注解:

        1.@AutoConfigurationPackage :自动配置包

//AutoConfigurationPackage的子注解
//Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    ...
}

        2.@Import({AutoConfigurationImportSelector.class})

        作用:自动配置导入选择器,给容器中导入一些组件

看到这里我们就能大概知道,SpringBoot自动配置重要的一些地方
        在启动类时通过@SpringBootApplication注解就开启了Spring Boot自动配置了一些库进入你的程序。而在该配置类内部,
1.@EnableAutoConfiguration注解内部 使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。 
2.配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些
3.配置类,初始化Bean 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean

 

实现一个自定义starter

        本文一直以整合Redsi为例,这里也实现一个与Redis相关的自定义启动器。

        需求: 自定义redis-starter,要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。

这里参考考导入mybatis坐标时,我们导入了一个mybatis-spring-boot-starter的启动器。这样在我们的

就能找到mybatis相关的jar包,不仅仅导入了mybatis-spring-boot-starter,还有mybatis-spring-boot-autoconfigure。

实现步骤:

1.创建一个基本的SpringBoot项目,在项目springboot_starter中先完成基本配置。

        当你在项目的pom.xml文件中导入坐标时,就开启了关于该坐标的相关自动配置。那么我们在实现自定义启动器时,也可以使用坐标。导入的坐标就是我们自定义的有关内容,我们观察pom.xml文件就能发现其实每一个项目都在其中有自己的坐标。那么我们就可以把自定义的redis-spring-boot-starter模块的坐标写入redis_starter中。

2.创建redis-spring-boot-starter模块,依赖redis-spring-boot-autoconfigure的模块

3.在redis-spring-boot-autoconfigure模块中初始化Jedis的Bean,并定义META-INF/spring.factories文件

在该模块中创建RedisAutoconfiguration配置类,注入jedis对象

为了动态的获取,创建一个application.yml在springboot_starter获取端口号、ip

在redis-spring-boot-autoconfigure中创建一个Redis身体

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private String host="localhost";
    private int port=6379;

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

 

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
    //注入jedis
    @Bean
    public Jedis jedis(RedisProperties redisProperties){
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}

怎样加载到这个类呢,就需要MEAT-INF文件

4.在测试模块中引入自定义的redis-starter依赖,测试获取Jedis的Bean,操作redis。

 获取到jedis对象证明成功实现了自定义启动时自动配置jedis.

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

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

相关文章

Python新手入门指南:从零开始学编程

欢迎来到Python的世界&#xff01;Python是一种功能强大、易于学习且用途广泛的编程语言。无论你是完全没有编程经验的新手&#xff0c;还是想要学习新技能的开发者&#xff0c;Python都是一个非常好的起点。接下来&#xff0c;我们将一起踏上这段编程之旅&#xff0c;从基础语…

机器学习第十一章-特征选择与稀疏学习

11.1子集收集与评价 属性称为"特征" &#xff0c;对当前学习任务有用的属性称为"相关特征" 、没什么用的属性称为"无关特 征" . 从给定的特征集合中选择出相关特征于集的过程&#xff0c;称为"特征选择"。 特征选择是一个重要的"…

Linux系统中的弹性计算功能

在当今数字化时代&#xff0c;弹性计算已经成为信息技术领域的重要概念之一。弹性计算指的是根据需要自动调整计算资源&#xff0c;以满足应用程序的需求。这种灵活性和自适应性使得弹性计算成为了云计算、大数据、人工智能等领域的核心技术之一。在这个领域中&#xff0c;Linu…

嵌入式软件--数电基础 DAY 4

1.SR锁存器 1》四种状态&#xff1a; S R Q Q set状态&#xff1a; 0 1 1 0 Reset状态&#xff1a; 1 0 0 1 维持状态&#xff1a; 1 1 维持上个状态 无意义状态…

VUE中出现Cannot find module ‘@/api/xxx.js‘ or its corresponding type declarations

在使用VSCode编写Vue程序时发现之前使用以下代码时却报出了错误 import {getEmployeeList} from /api/employee\ 保证文件地址正确且其中的方法也可以正常调用&#xff0c;只是报出了错误&#xff0c;该行代码上加入一个‘//ts-ignore’就可以解决。 修改后的代码 //ts-ig…

【mkdir rmdir】Centos/Linux mkdir rmdir命令详细介绍

【mkdir & rmdir】Centos/Linux mkdir & rmdir命令详细介绍 简介 mkdir rmdir 简介 mkdir 命令和 rmdir 命令是在 linux 当中比较常用的两个命令&#xff0c;这两个命令前者是创建空目录&#xff0c;后者是删除空目录。rmdir 命令的定位比较尴尬它的功能可以被 rm 命…

探索 Resolume Arena 7 - 引领 VJ 音视频创作的卓越软件

Resolume Arena 7 是一款专为 Mac 和 Windows 系统设计的强大 VJ 音视频软件&#xff0c;为创意专业人士和爱好者提供了丰富而出色的功能。 这款软件拥有直观且用户友好的界面&#xff0c;即使对于初学者来说&#xff0c;也能快速上手并开始创作。其强大的媒体管理功能&#x…

鸿蒙内核源码分析(物理内存篇) | 怎么管理物理内存

如何初始化物理内存? 鸿蒙内核物理内存采用了段页式管理&#xff0c;先看两个主要结构体.结构体的每个成员变量的含义都已经注解出来&#xff0c;请结合源码理解. #define VM_LIST_ORDER_MAX 9 //伙伴算法分组数量&#xff0c;从 2^0&#xff0c;2^1&#xff0c;...&a…

【JavaSec】反序列化初探(配合URLDNS)

JavaSec反序列化初探&#xff08;配合URLDNS&#xff09; 文章目录 JavaSec反序列化初探&#xff08;配合URLDNS&#xff09;基本demoMap入口类Java反射 基本demo 构建一个demo 实体类&#xff1a; package bli_seri;import java.io.Serializable;public class Person implem…

Crawlab 分布式部署指南:从 Scrapy 项目到单文件的全流程详解

crawlab分布式部署 远程服务器环境搭建 同之前gerapy分布式部署一样 添加服务器防火墙端口 redis&#xff1a;6379mysql&#xff1a;3306mogodb&#xff1a;27017scrapyd&#xff1a;6800crawlab&#xff1a;8080 访问crawlab服务&#xff1a;47.93.10.129 连接远程数据库 …

Unity Dots学习 (一)

先学习怎么使用&#xff0c;再研究底层代码。Dots大家都有所耳闻。一直没时间研究&#xff0c;最近研究一下 看上图可知&#xff0c;哪怕是CPU的第三级缓存也比内存要快2-5倍。 资料&#xff1a; 《DOTS之路》第零节——前导课(1)——DOTS的5W1H问题_哔哩哔哩_bilibili 《DOT…

javaweb的新能源充电系统pf

TOC springboot339javaweb的新能源充电系统pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可跨域…

SVG中的paint-order属性实现文字描边

过去只支持 SVG 元素 paint-order&#xff0c;表示绘制的顺序。 对于一个图形的绘制&#xff0c;顺序还是非常重要的。例如用SVG来绘制一个带边框的矩形 <style>rect{fill: #FFE8A3;stroke: #9747FF;stroke-width: 4;} </style><svg viewBox"0 0 300 30…

XSS-DOM

文章目录 源码SVG标签Dom-Clobbringtostring 源码 <script>const data decodeURIComponent(location.hash.substr(1));;const root document.createElement(div);root.innerHTML data;// 这里模拟了XSS过滤的过程&#xff0c;方法是移除所有属性&#xff0c;sanitize…

[数据集][图像分类]波色绝缘子缺失分类数据集1440张2类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;1440 分类类别数&#xff1a;2 类别名称:["missing","norma…

轻量高效的ControlNet开源 | ControlNetXt:支持主流生成架构,可与LoRA无缝集成!

当前的可控生成方法如ControlNet、Adapaters和ReferenceNet等通常需要大量额外的计算资源&#xff0c;尤其是对于视频生成&#xff0c;并且在训练中面临挑战或表现出较弱的控制力。 对此&#xff0c;港中文提出了一种轻量级可控模块&#xff1a;ControlNeXt&#xff0c;这是一…

PCIe Linux MRRS和MPS参数设置策略

1.概述 MPS&#xff08;Max Payload Size&#xff09;和MRRS&#xff08;Max Read Request Size&#xff09;共同影响PCIe总线的传输效率。如果MPS和MRRS设置的过小&#xff0c;传输相同长度的数据&#xff0c;需要更多的TLP报文&#xff0c;导致PCIe总线传输效率降低&#xf…

PHP多项目多场景排队叫号系统源码

&#x1f514;&#x1f4c8;多项目多场景排队叫号系统&#xff0c;让等待也高效有序&#xff01; 一、告别无序等待&#xff0c;智能排队新风尚 你是否曾在医院、银行或政务大厅等地方&#xff0c;面对冗长的队伍感到无奈&#xff1f;多项目多场景排队叫号系统&#xff0c;正…

Mybatis的分页,延迟加载和缓存

目录 分页&#xff1a; 方式一&#xff1a;利用 limit 实现物理分页 利用limit的关键字分页 方式二&#xff1a;RowBounds集合逻辑分页 方式三&#xff1a;插件分页 延迟加载和立即加载&#xff1a; 什么是立即加载&#xff1a; 什么是延迟加载 延迟加载的配置 缓存&a…

XSS漏洞洞讲解

目录 一、XSS漏洞的定义 1.什么是XSS漏洞&#xff1f; 二、XSS漏洞的类型 1.反射型 XSS 2.DOM型 XSS 3.存储型 XSS 三、实战案例演练 第1关 Ma Spaghet 第2关 Jefff 第3关 Ugandan Knuckles 第4关 Ricardo Milos 第5关 Ah Thats Hawt 第6关 Ligma ​第7关 Mafia …