Spring Boot(二)— 自定义Spring Boot Starter

news2024/11/17 17:51:04

 在Spring Boot中,自定义Spring Boot Starter是一个常见且强大的功能,它允许开发者为特定的功能或库创建自己的自动配置,从而简化集成过程。

1 前置知识

Spring Boot的事件为应用的启动和关闭提供了详细的上下文信息,使得开发者能够根据不同阶段执行特定的逻辑。

Spring Boot的外部化配置是一种允许开发者将应用程序的配置信息从代码中分离出来,存储在外部文件或系统中的机制。支持包括Java属性文件、YAML文件、环境变量和命令行参数等多种外部配置源。

1.1 事件与监听器

ApplicationStartingEvent

程序刚启动,但没进行任何实际工作,除了监听器和初始化器的注册。

ApplicationEnvironmentPreparedEvent

应用程序的环境(例如配置属性、命令行参数等)已准备好,但上下文还没创建。

ApplicationPreparedEvent

属性之前,bean定义加载之后。(刷新指上下文的初始化过程,包括加载bean定义等)

ContextRefreshedEvent

刷新之后,ApplicationStartedEvent之前。

WebServerInitializedEvent

当基于Servlet的Web服务器(如tomcat)初始化完成后。(ApplicationStartedEvent之前

ApplicationStartedEvent

刷新之后,任何启动任务或命令行指令被调用之前。

ApplicationReadyEvent

任何应用程序和命令行运行器调用之后执行。

ApplicationFailedEvent

启动时发生异常。

表 Spring Boot 定义的事件

上面大多数事件都是在bean之前创建的,因此如果将监听器设置为bean,可能没法正常的监听,可以通过application.addListeners方法来添加监听器。

1.2 外部化配置

属性值可以通过多种方式注入到应用程序中,常见的方式有:1)使用@Value注解将属性值直接注入到Bean中。2)使用@ConfigurationProperties可以将配置属性绑定到结构化对象上。

属性值类型可以是数组、Map、Java对象等复杂的类型。还可以对属性值进行校验,需要添加依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@ConfigurationProperties("student")
@Data
@Validated
public class Student {
    @NotBlank(message = "姓名不能为空")
    private String name;
    private Integer age;
    private List<Address> addressList;
    private List<String> role;
    private Map<String,Object> map;
    @DurationUnit(ChronoUnit.HOURS)
    private Duration duration1;
    private Duration duration2;
    private Period period1;
    private Period period2;
    private Date date;
    private String username;

    @Data
    private static class Address {
        private String province;
        private String city;
    }
}
student:
  name: 黄先生
  age: 27
  role:
    - 爸爸
    - 丈夫
    - 员工
  map:
    info: hello
    username: hmf
  addressList:
    - province: 广东省
      city: 深圳市
  duration1: 900
  duration2: 15m
  period1: p3D
  period2: p20M
  date: 2023/03/02 12:00
  username: hmf

2 自动配置

Spring Boot的自动配置是其核心特性之一,大大简化了Spring应用的初始搭建以及开发过程。基于“约定优于配置”的思想,通过默认配置来减少开发者需要手动进行的配置工作。

2.1 Bean 创建的条件约束

在自动配置过程中,可以使用一个或多个约束来控制bean的创建。

@ConditionalOnClass

指定类在运行时才会创建bean,(注意引用类在编译时可能存在,但是运行时不一定会存在。)

@ConditionalOnBean

当指定的bean存在时才会创建bean。注意指定的bean 一般为业务代码中的bean,而非依赖包中的bean。这个会受到bean创建顺序的影响,依赖包中的bean一般后于业务代码中的bean创建。

@ConditionalOnMissingBean

当这个bean不存在时才会被创建。

@ConditionalOnProperty

当对于的属性名存在特定值时bean才会被创建。

表 常用的Bean条件约束

2.2 自定义Starter

创建自定义的Starter,步骤如下:

  1. 创建一个Maven项目,该项目包含了核心功能。
  2. 创建一个自动配置Maven模块,命名规则一般为:项目名-spring-boot-autoconfigure。该模块的作用为:创建用于封装配置项的配置类,可以使用@ConfigurationProperties注解来指定配置项的前缀;创建自动配置类xxxGenerateAutoConfiguration,该类包含了根据条件自动创建Bean的逻辑;配置META- INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,该文件包含了自定配置类的全限定名,Spring Boot在启动时能自动加载。
  3. 创建一个空Maven模块,命名规则一般为:项目名-spring-boot-starter,该模块的作用为在pom文件中,引入核心模块、自动配置模块等需要的依赖。
  4. 打包部署。

2.2.1 自动配置模块中的pom.xml

在自动配置模块中,引入依赖时,一般会加上<optional>true</optional>,这意味着依赖不会被传递性地引入到使用这个库到其他项目中,有以下的好处:

  1. 避免依赖冲突。如果两个不同的Starter都依赖了不同版本的同一库,而且没有将其声明为可选依赖,那么构建过程中就可能会遇到依赖版本冲突的问题。
  2. 控制依赖范围。有时只想让Starter负责自动配置功能,而不希望它强制引入一些可能不需要的运行时依赖。通过将依赖声明为可选,允许最终用户根据自己的需求选择是否引入这些额外的依赖。
  3. 模块化和解耦。让项目不会因为引入Starter而间接引入不必要的依赖、减少最终构建的应用程序的大小,有助于优化应用程序的启动时间和性能。

2.2.2 自动配置模块中外部化配置的元数据描述

Spring Boot启动时,它会扫描类路径下的META-INF目录来查找spring-autoconfigure-metadata.properties 文件,该文件会被加载并用于生成关于自动配置类的元数据信息(属性的描述、类型、默认值、是否必须等信息)。

这个文件,一般可以通过添加spring-autoconfigure-metadata.properties依赖来自动生成,或者可以创建additional-spring-configuration-metadata.json 文件来自定义(这种方式不建议,还是推荐自动生成的形式)。

@Configuration
@EnableConfigurationProperties({CustomProperties.class,CustomSellerProperties.class})
public class CustomAutoConfiguration {

    private final CustomProperties customProperties;
    private final CustomSellerProperties customSellerProperties;

    public CustomAutoConfiguration(CustomProperties customProperties, CustomSellerProperties customSellerProperties) {
        this.customProperties = customProperties;
        this.customSellerProperties = customSellerProperties;
    }

    @Bean
    @ConditionalOnMissingBean
    public Shop shop() {
        Shop shop = new Shop();
        shop.setManagerName(customProperties.getManagerName());
        shop.setName(customProperties.getName());
        shop.setCreateDate(customProperties.getCreateDate());
        shop.setGoodsList(customProperties.getGoodsList());
        shop.setStateOwned(customProperties.getStateOwned());
        return shop;
    }

    @Bean
    @ConditionalOnMissingBean
    public Seller seller() {
        Seller seller = new Seller();
        seller.setName(customSellerProperties.getName());
        seller.setAge(customSellerProperties.getAge());
        return seller;
    }

    @Bean
    @ConditionalOnBean(name = "myBean")
    public DataSource1 dataSource1() {
        return new DataSource1();
    }

    @Bean
    @ConditionalOnProperty(name = "custom-seller.age", havingValue = "30")
    public DataSource2 dataSource2() {
        return new DataSource2();
    }
}

@Configuration
@ConditionalOnClass(CustomDataSource.class)
public class CustomDataAutoConfiguration {

    public CustomDataAutoConfiguration() {
        System.out.println("CustomDataAutoConfiguration 被创建");
    }

    @Bean
    @ConditionalOnMissingBean
    public CustomDataSource customDataSource() {
        return new CustomDataSource();
    }

}

@ConfigurationProperties(prefix = "custom")
public class CustomProperties {

    private String managerName;

    private String name;

    private Date createDate;

    private List<String> goodsList;

    private Boolean stateOwned;

    public String getManagerName() {
        return managerName;
    }

    public void setManagerName(String managerName) {
        this.managerName = managerName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public List<String> getGoodsList() {
        return goodsList;
    }

    public void setGoodsList(List<String> goodsList) {
        this.goodsList = goodsList;
    }

    public Boolean getStateOwned() {
        return stateOwned;
    }

    public void setStateOwned(Boolean stateOwned) {
        this.stateOwned = stateOwned;
    }
}

@ConfigurationProperties(prefix = "custom-seller")
public class CustomSellerProperties {

    /**
     * 商家姓名哦
     */
    private String name = "黄sire";

    private Integer age = 18;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.huangmingfu.autoconfigure.CustomAutoConfiguration
com.huangmingfu.autoconfigure.CustomDataAutoConfiguration

自动配置模块中的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>3.1.2</version>
    </parent>

    <groupId>com.huangmingfu</groupId>
    <artifactId>custom-spring-boot-autoconfigure</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.huangmingfu</groupId>
            <artifactId>custom-core</artifactId>
            <optional>true</optional>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.huangmingfu</groupId>
            <artifactId>custom-data</artifactId>
            <version>1.0-SNAPSHOT</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

图 自动配置模块依赖中自动生成的元数据描述文件 

图 使用这个starter依赖的主服务的yml 配置文件

注意:对于配置属性的默认值,在元数据描述文件中定义的默认值并不会生效,可以在这个配置类中定义默认值(自动生成元数据描述文件时,默认值也取自这)。

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

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

相关文章

【脚本】多功能Ubuntu临时授予用户sudo权限管理工具

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 设计原理和初衷可以看这里&#xff1a;【技巧】Ubuntu临时授予用户sudo权限&#xff0c;并在一定时间后自动撤销_ubuntu jianshao sudo-CSDN博客文章浏览阅读404次。非常实用_ubuntu jianshao sudohttps://blog.c…

mysql dll文件的缺失和Can‘t connect to MySQL server on ‘localhost‘ (10061)

个人笔记&#xff08;整理不易&#xff0c;有帮助&#xff0c;收藏点赞评论&#xff0c;爱你们&#xff01;&#xff01;&#xff01;你的支持是我写作的动力&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔…

系统架构最佳实践 -- API网关架构设计

目录 1.什么是API网关&#xff1f; 2.API网关的核心功能 3.架构设计原则 4.API网关的实现方式 5.常见的API网关工具和框架 6.实际案例分析 API网关是现代微服务架构中的重要组件&#xff0c;它充当了前端和后端微服务之间的中介。本文将介绍API网关的架构设计原则和实现方…

Textarea的常用属性thymeleaf

文章目录 textareathymeleaf1.基础使用2.代码块的切换3.链接表达式1&#xff09;范例 4.前后端5.遍历1.th:each2.th:switch3.添加属性 组件替换 每周总结 textarea -webkit-scrollbar&#xff1a;width&#xff1a;0&#xff1b;让滚动条隐藏&#xff0c;宽度为0 resize&#x…

RUST语言字符串与字符数组操作

1.字符串转换为字符数组: //分配内存存储HellWorld并使用s指针指向该地址let mut s String::from("HelloWorld");println!("s> {}",s);//转换字符串为字符数组let bs.as_bytes(); 2.遍历字符数组 //遍历字符数组for c in b {println!("char of…

C语言简单的数据结构:单链表的有关算法题(2)

题目&#xff1a; 4. 单链表相关经典算法OJ题3&#xff1a;合并两个有序链表5. 循环链表经典应⽤-环形链表的约瑟夫问题6. 单链表相关经典算法OJ题5&#xff1a;分割链表 接着我们介绍后面的三道题&#xff0c;虽然代码变多了但我们的思路更加通顺了 4. 单链表相关经典算法OJ题…

DC-5渗透测试复现

DC-5渗透测试复现 目的&#xff1a; 获取最高权限以及5个flag 过程&#xff1a; 信息打点-文件包含漏洞-弹shell- scren-4.0.5提权 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.136) 靶机&#xff1a;DC_3(192.168.85.134) 复现&#xff1a; 一.信息收集 nma…

MES生产管理系统:私有云、公有云与本地化部署的比较分析

随着信息技术的迅猛发展&#xff0c;云计算作为一种新兴的技术服务模式&#xff0c;已经深入渗透到企业的日常运营中。在众多部署方式中&#xff0c;私有云、公有云和本地化部署是三种最为常见的选择。它们各自具有独特的特点和适用场景&#xff0c;并在不同程度上影响着企业的…

EEG-GCNN 论文问题整理

auc是什么&#xff1f; AUC是指接收者操作特征曲线&#xff08;ROC曲线&#xff09;下的面积&#xff0c;用于评估分类模型的性能。AUC的取值范围在0到1之间&#xff0c;越接近1表示模型的性能越好&#xff0c;越接近0.5表示模型的性能越差。AUC的计算方法是通过计算ROC曲线下…

HiveQL练习(hive3.x)

零、准备工作 1. Hive环境安装 参见搭建Hive 3.x环境&#xff08;CentOS 9 Hadoop3.x&#xff09; 2. 准备数据 在虚拟机HOME目录创建如下文件内容&#xff1a; cd /root vi emp.csv内容如下&#xff1a; 7369,SMITH,CLERK,7902,1980/12/17,800,,20 7499,ALLEN,SALESMAN…

基于STM32的快递小车无人驾驶系统

目录 摘 要 一、绪论 1.1 背景和意义 1.1.1 背景 1.1.1 意义 1.2 国内外研究现状 二、小车设计方案 2.1 方案一 2.2 方案二 2.3 方案三 2.4 方案选择与论证 三、硬件设计方案 3.1 单片机最小系统 3.2 光耦隔离电路 3.3 电源模块电路 3.4 直流电机驱动电路 3.…

Lua脚本使用手册(Redis篇)

Lua脚本 **简介&#xff1a;**Lua是一种功能强大的&#xff0c;高效&#xff0c;轻量级&#xff0c;可嵌入的脚本语言。它是动态类型语言&#xff0c;通过使用基于寄存器的虚拟机解释字节码运行&#xff0c;并具有增量垃圾收集的自动内存管理&#xff0c;是配置&#xff0c;脚…

数据资产与数据要素的重要性及数据资产入表的实践指南

## 引言在当今快速发展的数字化时代&#xff0c;数据资产已经成为企业最宝贵的资源之一。数据资产不仅对企业的运营决策有着至关重要的影响&#xff0c;而且在企业的财务健康和市场竞争力方面扮演着核心角色。数据要素&#xff0c;作为构成数据资产的基本单元&#xff0c;其管理…

【攻防世界】bug

垂直越权IP绕过文件上传 文件上传绕过&#xff1a; 1. mime检测 2. 大小写绕过 3. 等价替换&#xff08;php5&#xff0c;php3&#xff09; 4. 利用JavaScript执行php代码&#xff08;正常的php代码会被检测到&#xff0c;所以就用JavaScript来执行&#xff09; <script lan…

终端工具命令行颜色配置(解决终端工具连上服务器之后,无颜色问题)

本期主题&#xff1a; 讲解使用mobaxterm等终端工具连上服务器&#xff0c;但是命令行没有颜色的问题 目录 1. 问题描述2. 原因解释3.测试 1. 问题描述 使用终端工具&#xff08;Mobaxterm等&#xff09;连上服务器之后&#xff0c;发现终端工具没有颜色&#xff0c;如下图&am…

数模 线性规划模型理论与实践

线性规划模型理论与实践 1.1 线性规划问题 在人们的生产实践中&#xff0c;经常会遇到如何利用现有资源来安排生产&#xff0c;以取得最大经济效益的问题。此类问题构成了运筹学的一个重要分支一数学规划&#xff0c;而线性规划(Linear Programming 简记LP)则是数学规划的一个…

深度学习学习日记4.14 数据增强 Unet网络部分

数据增强 transforms.Compose([&#xff1a;这表示创建一个转换组合&#xff0c;将多个数据转换操作串联在一起 transforms.RandomHorizontalFlip()&#xff1a;这个操作是随机水平翻转图像&#xff0c;以增加数据的多样性。它以一定的概率随机地水平翻转输入的图像。 transfo…

系统架构最佳实践 -- 金融企业的资损防控

一、资损产生的原因 由于支付行业的特殊性与复杂性&#xff08;主要处理资金相关业务&#xff09;&#xff0c;支付公司处于资损的风口浪尖&#xff0c;最容易发生资损&#xff0c;可以说资损风险无处不在。 常规来说&#xff0c;资损原因主要可以分为以下三类&#xff1a; 1…

Linux设备驱动开发-字符设备

阅读引言&#xff1a; 从linux文件的种类、字符设备的创建、设备号、申请设备号、cdev对象和字符设备的对应关系、应用层调用到我们编写的设备驱动方法合集的流程。 目录 一、Linux文件的种类 二、Linux对设备的分类 三、驱动程序如何向应用层提供接口 四、Linux中设备的划…

微服务之LoadBalancer负载均衡服务调用

一、概述 1.1什么是负载均衡 LB&#xff0c;既负载均衡&#xff08;Load Balancer&#xff09;,是高并发、高可用系统必不可少的关键组件&#xff0c;其目标是尽力将网络流量平均分发到多个服务器上&#xff0c;以提高系统整体的响应速度和可用性。 负载均衡的主要作用 高并发…