程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Spring Boot

    • Spring Boot - 自动配置
    • Spring Boot - 自定义starter
      • 一、什么是SpringBoot starter机制
      • 二、为什么要自定义starter
      • 三、什么时候需要创建自定义starter
        • 1. 通用功能模块的集成
        • 2. AOP日志管理
        • 3. 解决特定问题的工具
        • 4. 微服务环境下的配置共享
        • 5. 提高开发效率
      • 四、自动加载核心注解说明
        • 4.1 核心条件注解
        • 4.2 加载顺序和优先级
      • 五、自定义starter的开发流程
        • 案例一:为短信发送功能创建一个starter
        • 1. 创建Starter项目
        • 2. 编写相关属性类(XxxProperties):SmsProperties.java
        • 3. 编写Starter项目的业务功能
        • 4. 自定义Condition条件(可选)
        • 5. 编写自动配置类AutoConfig
        • 6. 编写spring.factories文件加载自动配置类
        • 7. 使用@EnableXxx机制(可选)
        • 8. 打包安装
        • 9. 在其他项目中的应用
        • 案例二:AOP方式统一服务日志
        • 1. 导入aop相关依赖
        • 2. 编写相关属性类(XxxProperties):WebLogProperties.java
        • 3. 编写Starter项目的业务功能
        • 4. 编写自动配置类AutoConfig
        • 5. 编写spring.factories文件加载自动配置类
        • 6. 打包,操作同上
        • 7.在其他项目中引用
    • Spring Boot - 配置文件
    • Spring Boot - 自定义SpringApplication
    • Spring Boot - 生命周期与事件
    • Spring Boot - 事件驱动
    • Spring Boot - Bean 加载方式
    • Spring Boot - 容器资源感知与获取
    • Spring Boot - 定时任务
    • Spring Boot - 异步任务
    • Spring Boot - 内置日志
    • Spring Boot - 函数式 Web
    • Spring Boot - 响应式远程调用
    • Spring Boot - 接口文档
    • Spring Boot - 单元测试
    • Spring Boot - 内容协商
    • Spring Boot - 参数校验
    • Spring Boot - HTTP客户端工具
    • Spring Boot - 控制器请求映射
    • Spring Boot - 请求参数接收
    • Spring Boot - 通用响应类
    • Spring Boot - 全局异常处理
    • Spring Boot - 整合Druid
    • Spring Boot - 整合Thymeleaf
    • Spring Boot - 国际化实现
    • Spring Boot - 自定义注解
  • Spring高级
  • Spring Boot
scholar
2024-03-29
目录

Spring Boot - 自定义starter

# 一、什么是SpringBoot starter机制

SpringBoot中的starter是一种非常重要的机制(自动化配置),能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。

starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。SpringBoot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。

所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。

# 二、为什么要自定义starter

在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。

如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,复用的时候只需要将其在pom中引用依赖即可,SpringBoot为我们完成自动装配,简直不要太爽。

# 三、什么时候需要创建自定义starter

在我们的日常开发工作中,可能会需要开发一个通用模块,以供其它工程复用。SpringBoot就为我们提供这样的功能机制,我们可以把我们的通用模块封装成一个个starter,这样其它工程复用的时候只需要在pom中引用依赖即可,由SpringBoot为我们完成自动装配。

常见场景:

# 1. 通用功能模块的集成

如果你有一些功能模块,如短信发送服务,这些功能在多个项目中都需要使用,你可以将这些通用功能封装成一个starter。这样,其他项目只需要简单地添加一个Maven依赖就能集成该功能,同时享受到Spring Boot的自动配置特性。

# 2. AOP日志管理

利用AOP(面向切面编程)技术实现的日志管理,比如自动记录方法的执行时间、参数和返回值,是许多应用都需要的功能。将这样的AOP逻辑封装成一个starter可以使得在任何新项目中引入和使用变得非常简单。

# 3. 解决特定问题的工具

对于某些特定的技术问题,如分布式系统中的ID生成策略(例如雪花ID),或者在使用数据序列化/反序列化库(如Jackson或Fastjson)处理Long类型到String类型转换以解决精度问题时,一个专门针对这些问题的starter可以提供一个统一且可重用的解决方案。

# 4. 微服务环境下的配置共享

在微服务架构中,可能会有一些跨服务的共享配置,如数据库连接池配置和Redis配置。创建一个或多个starter,用于封装这些共享配置和服务(例如,统一的RedisTemplate配置),可以大幅简化服务的配置和维护。

# 5. 提高开发效率

当你发现在多个项目中重复实现相同的配置或模板代码时,通过创建自定义starter来封装这些共通部分,不仅可以减少冗余、提高代码的可维护性,还可以加快新项目的开发进度。

总结来说,当你面对需要在多个项目之间共享的通用功能、配置问题,或者需要提供一种标准化的集成方式时,创建自定义starter便显得非常有价值。它不仅提高了开发效率,还有助于保持项目的一致性和可维护性。

# 四、自动加载核心注解说明

在Spring Boot中,条件注解(Conditional Annotations)允许在特定条件满足时进行自动配置。这些条件可以基于类路径上的类存在、Bean的存在、配置属性的值、环境特性等因素。理解这些注解及其加载顺序对于高效地使用和创建Spring Boot自定义starters至关重要。

# 4.1 核心条件注解

Spring Boot的条件注解是一套强大的工具,允许开发者在满足特定条件时自动配置beans。这种灵活性特别适用于创建模块化和可配置的应用程序,以及开发自定义starters。以下是对核心条件注解的详细总结:

  1. @Conditional
  • 基本用法:它是条件注解的基础,可以实现基于特定条件的bean注册。通常与自定义的条件类一起使用,这些条件类实现了Condition接口,并重写matches方法以定义满足条件的逻辑。
  • 使用场景:当需要基于复杂或非标准条件进行bean注册时,比如组合多个条件或创建全新的判断逻辑。
  1. @ConditionalOnBean / @ConditionalOnMissingBean
  • 基本用法:这两个注解控制基于Spring上下文中特定Bean的存在或缺失来进行自动配置。
  • @ConditionalOnBean:当Spring上下文中存在一个或多个指定的beans时,条件满足。
  • @ConditionalOnMissingBean:当Spring上下文中缺少一个或多个指定的beans时,条件满足。
  • 使用场景:用于在特定bean的依赖存在时启用配置或在缺少时提供默认配置。
  1. @ConditionalOnClass / @ConditionalOnMissingClass
  • 基本用法:根据类路径上的类的存在或缺失来控制配置。
  • @ConditionalOnClass:当类路径上存在指定的类时,条件满足。
  • @ConditionalOnMissingClass:当类路径上不存在指定的类时,条件满足。
  • 使用场景:常用于基于特定库的存在来启用自动配置,例如只有当某个特定的库类存在时才自动配置与之相关的beans。
  1. @ConditionalOnProperty
  • 基本用法:根据Spring环境中的属性存在或匹配特定值来控制配置。
  • 使用场景:用于在配置文件中设置的属性值满足特定条件时启用或禁用配置。
  1. @ConditionalOnExpression
  • 基本用法:基于SpEL(Spring Expression Language)表达式的评估结果来控制配置。
  • 使用场景:当需要基于更复杂的条件逻辑来启用配置时使用,比如依赖于多个环境属性或调用方法的结果。
  1. @ConditionalOnResource
  • 基本用法:当类路径上存在特定资源文件时,条件满足。
  • 使用场景:用于基于资源文件的存在来启用配置,例如仅当特定的配置文件存在于类路径上时才加载相关配置。
  1. @ConditionalOnWebApplication / @ConditionalOnNotWebApplication
  • 基本用法:根据应用是否为Web应用来控制配置。
  • @ConditionalOnWebApplication:当应用是一个Web应用时,条件满足。
  • @ConditionalOnNotWebApplication:当应用不是一个Web应用时,条件满足。
  • 使用场景:用于仅在Web应用上下文或非Web应用上下文中启用特定配置。
  1. @ConditionalOnJava
  • 基本用法:根据JVM的版本来控制配置。
  • 使用场景:用于只有在特定版本的Java环境下才启用的配置。
  1. @ConditionalOnJndi
  • 基本用法:在JNDI资源存在时条件满足。
  • 使用场景:用于只有在JNDI资源可用时才启用的配置,常见于需要连接到企业级服务如数据库和消息服务时。

# 4.2 加载顺序和优先级

Spring Boot的自动配置通过@EnableAutoConfiguration注解启用,该注解通过spring.factories文件加载应用的自动配置类。自动配置类的加载和处理顺序受以下因素影响:

  • Auto-configuration类的定义顺序:在spring.factories中列出的自动配置类按列出的顺序进行处理。
  • @AutoConfigureAfter 和 @AutoConfigureBefore:这两个注解可以控制自动配置类之间的相对顺序。通过这些注解,你可以声明一个配置类应该在另一个配置类之前或之后加载。
  • 条件评估:Spring Boot在处理自动配置类时会评估每个类上的条件注解。只有当所有相关条件都满足时,配置类中定义的Bean才会被注册到Spring上下文中。

# 五、自定义starter的开发流程

那么前面对自定义 starter 机制简单介绍了一下,接下来就进入正题!

自定义starter的开发流程:

  1. 创建Starter项目(spring-initl 2.1.14)
  2. 定义Starter需要的配置类(Properties)
  3. 编写Starter项目的业务功能
  4. 编写自动配置类
  5. 编写spring.factories文件加载自动配置类
  6. 打包安装
  7. 其它项目引用

# 案例一:为短信发送功能创建一个starter

# 1. 创建Starter项目

1.1 命名规范

  • 官方Starter命名:遵循spring-boot-starter-{模块名}的格式,如spring-boot-starter-web。
  • 自定义Starter命名:采用{模块名}-spring-boot-starter的格式,例如,如果你的模块名为mystarter,则命名为mystarter-spring-boot-starter。

1.2 必须引入的依赖

对于自定义starter,通常需要引入spring-boot-configuration-processor依赖,此依赖用于处理配置属性类,生成元数据信息,从而支持属性的自动提示功能。使用<optional>true</optional>标记,表示这个依赖在引用此starter的项目中不是必须的。

<!--表示两个项目之间依赖不传递;不设置optional或者optional是false,表示传递依赖-->
<!--例如:project1依赖a.jar(optional=true),project2依赖project1,则project2不依赖a.jar-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
   <optional>true</optional>
</dependency> 
1
2
3
4
5
6
7

# 2. 编写相关属性类(XxxProperties):SmsProperties.java

  • 使用@ConfigurationProperties(prefix = "zzcloud.sms")注解来定义属性类,prefix指定了配置文件中的属性前缀。
  • 类的属性名称需要根据Spring Boot的宽松绑定规则与配置文件中的属性相匹配。
  • 可以通过简单地初始化字段值来定义默认值。
  • 属性类可以设置为包私有,但其字段需要有公共的setter方法,以便Spring Boot能够进行绑定。

注意事项

在初步创建SmsProperties类时,可能会看到IDE提示错误,提示未通过@EnableConfigurationProperties注册或未标记为Spring组件。这是因为还需要创建自动配置类并使用@EnableConfigurationProperties(SmsProperties.class)来启用对SmsProperties类的配置属性绑定。

package com.zking.zzcloudspringbootstarter.sms;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.io.Serializable;

/**
 * SmsProperties类通过@ConfigurationProperties注解绑定配置属性,
 * 以"zzcloud.sms"作为前缀。这个类允许Spring Boot应用在application.properties
 * 或application.yml中配置SMS服务的访问密钥和密钥ID。
 */
@ConfigurationProperties("zzcloud.sms")
public class SmsProperties implements Serializable {

    // 访问ID,用作访问SMS服务的账号
    private String accessKeyId;

    // 访问凭证,用作访问SMS服务的密码
    private String accessKeySecret;

    // 获取访问ID的公共方法
    public String getAccessKeyId() {
        return accessKeyId;
    }

    // 设置访问ID的公共方法
    public void setAccessKeyId(String accessKeyId) {
        this.accessKeyId = accessKeyId;
    }

    // 获取访问凭证的公共方法
    public String getAccessKeySecret() {
        return accessKeySecret;
    }

    // 设置访问凭证的公共方法
    public void setAccessKeySecret(String accessKeySecret) {
        this.accessKeySecret = accessKeySecret;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 3. 编写Starter项目的业务功能

在自定义starter项目中,开发业务功能是核心部分之一。对于SMS服务的starter,需要先定义一个服务接口(如ISmsService),以及实现这个接口的实现类(SmsServiceImpl)。

package com.zking.zzcloudspringbootstarter.sms;

/**
 * 短信服务接口定义。
 * 该接口提供发送短信的基本功能,包括指定手机号、短信签名、模板以及内容。
 */
public interface ISmsService {
 
    /**
     * 发送短信的方法。
     *
     * @param phone 手机号码,短信发送的目标手机号。
     * @param signName 短信签名,用于标识发送短信的个体或企业,需在短信服务平台注册。
     * @param templateCode 短信模板代码,指定发送短信使用的模板,需在短信服务平台预设。
     * @param data 短信内容,根据短信模板填充的具体内容,通常以JSON格式提供。
     */
    void send(String phone, String signName, String templateCode, String data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.zking.zzcloudspringbootstarter.sms;

// 引入短信服务接口
import com.zking.zzcloudspringbootstarter.sms.ISmsService;

/**
 * 短信服务的实现类,实现了ISmsService接口,提供发送短信的功能。
 */
public class SmsServiceImpl implements ISmsService {
 
    // 访问ID,即账号。用于访问短信服务提供商的API。
    private String accessKeyId;
    // 访问凭证,即密码。用于验证访问身份。
    private String accessKeySecret;
 
    /**
     * 类的构造函数,用于初始化短信服务所需的访问ID和访问凭证。
     *
     * @param accessKeyId 访问ID
     * @param accessKeySecret 访问凭证
     */
    public SmsServiceImpl(String accessKeyId, String accessKeySecret) {
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
    }
 
    /**
     * 实现接口中的send方法,用于发送短信。
     *
     * @param phone 手机号码,接收短信的目标手机号。
     * @param signName 短信签名,用于标识发送者。
     * @param templateCode 短信模板代码,指定使用哪个模板发送短信。
     * @param data 短信内容,根据模板填充的具体内容。
     */
    @Override
    public void send(String phone, String signName, String templateCode, String data) {
        // 打印访问信息,实际开发中应替换为调用短信服务提供商的API进行短信发送。
        System.out.println("接入短信系统,accessKeyId=" + accessKeyId + ",accessKeySecret=" + accessKeySecret);
        System.out.println("短信发送,phone=" + phone + ",signName=" + signName + ",templateCode=" + templateCode + ",data=" + data);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 4. 自定义Condition条件(可选)

如果想确保只有在提供了accessKeyId和accessKeySecret时才启用SMS服务,可以通过检查这些属性是否被设置为非空来实现。不过,@ConditionalOnProperty不能直接用于检查属性值是否非空,它更多是用于检查属性是否存在或等于特定值。

一个更灵活的方式是实现自定义条件。可以创建一个实现了Condition接口的类,这个类包含了决定是否创建bean的逻辑。这种方法可以让你基于accessKeyId和accessKeySecret是否被定义来决定是否创建SmsServiceImpl。

public class SmsServiceCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        // 尝试通过不同的命名约定获取属性值
        String accessKeyId = Stream.of("zzcloud.sms.accessKeyId", "zzcloud.sms.access-key-id",)
                .map(env::getProperty)
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);
        String accessKeySecret = Stream.of("zzcloud.sms.accessKeySecret", "zzcloud.sms.access-key-secret")
                .map(env::getProperty)
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);
        return accessKeyId != null && !accessKeyId.isEmpty() && accessKeySecret != null && !accessKeySecret.isEmpty();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

我们直接通过Environment接口使用getProperty方法获取属性值时,需要确保使用的属性名与配置文件中的完全匹配,为了确保适配不同的配置文件,提供了两种匹配模式去确保成功获取属性值。

然后,在SmsAutoConfig类(自动配置类)中使用@Conditional注解来引用这个自定义条件

# 5. 编写自动配置类AutoConfig

5.1 使用@Configuration注解

这个注解表明该类是一个配置类,Spring Boot会在启动时自动扫描并加载这个类的配置信息。

5.2 应用@EnableConfigurationProperties

自定义Spring Boot starter的自动配置类SmsAutoConfig,它负责基于SmsProperties中定义的属性创建并配置SmsServiceImpl bean。

package com.zking.zzcloudspringbootstarter.config;

import com.zking.zzcloudspringbootstarter.sms.SmsProperties;
import com.zking.zzcloudspringbootstarter.sms.SmsServiceImpl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * SMS自动配置类,负责创建和配置SMS服务的bean。
 */
@Configuration // 标记这个类是一个配置类,Spring Boot会在启动时自动扫描并加载它
// 使SmsProperties类上的@ConfigurationProperties注解生效,允许从配置文件中绑定属性值
@EnableConfigurationProperties({SmsProperties.class}) 
public class SmsAutoConfig {

    @Resource
    private SmsProperties smsProperties; 

    /**
     * 定义一个SmsServiceImpl的Bean。
     * 使用SmsProperties中的属性来初始化SmsServiceImpl实例。
     *
     * @return SmsServiceImpl的实例,配置了访问ID和访问凭证。
     */
    @Bean
    @Conditional(SmsServiceCondition.class) // 使用自定义条件
    public SmsServiceImpl smsServiceImpl(){
        // 创建SmsServiceImpl的实例,通过SmsProperties提供的访问ID和访问凭证进行初始化
        return new SmsServiceImpl(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

通过自定义的这种条件,SmsServiceImpl的bean只有在accessKeyId和accessKeySecret属性被正确配置时才会被创建。如果没有这个属性或者属性值为空,则不会创建该bean,从而避免了不必要的资源消耗。

注意事项

  • 在自己的项目中使用时,如果@ConfigurationProperties类被标记为一个组件(加上@Component注解),那么不需要显式使用@EnableConfigurationProperties。
  • 在自定义starter或自动配置类中,@ConfigurationProperties类不会被标记为一个组件,@EnableConfigurationProperties注解确保了即使属性类没有被标记为组件(@Component),Spring Boot也会实例化并注入配置属性。

# 6. 编写spring.factories文件加载自动配置类

6.1 创建文件

在resources/META-INF目录下新建spring.factories文件,这个文件是Spring Boot自动配置机制的关键,它指示Spring Boot在启动时加载指定的自动配置类。

6.2 配置自动配置类

在spring.factories文件中,指定你的自动配置类的完整类名,以便Spring Boot能够加载它。

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zking.zzcloudspringbootstarter.sms.SmsAutoConfig
1
2
3

注意:AutoConfig在这里指的是你的自动配置类的类名。如果有多个自动配置类,可以使用逗号分隔每个类的完整类名,或者使用反斜杠\来继续在新行列出其他类名。如下图所示

 # Auto Configure(以MybatisPlus为例)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
1
2
3
4

通过spring.factories实现的全自动配置

当你在spring.factories文件中指定了自动配置类,Spring Boot启动时会自动检测并加载这些配置类,前提是starter已经被加入到项目的依赖中。这个机制不需要在应用的任何地方显式声明或使用注解来启用配置,符合"约定大于配置"的原则,提供了无缝的自动配置体验。

# 7. 使用@EnableXxx机制(可选)

你还以通过自定义注解手动导入自动配置类来实现半自动化的配置,这种方法提供了更多的控制权,允许开发者显式地启用某些自动配置。这种做法依赖于Spring的@Import注解来实现,与自定义的启用注解(如@EnableXxx)结合使用。

首先,你需要定义一个自定义注解,比如@EnableSms,用于在需要这个starter功能的Spring Boot应用中启用SMS功能。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(SmsAutoConfig.class) // 这里导入自动配置类
public @interface EnableSms {
}
1
2
3
4
5
6

这个注解通过@Import引入了SmsAutoConfig自动配置类,使得只要在Spring Boot应用的任一配置类上使用了@EnableSms,相关的自动配置就会被应用(一般我们会放在启动类上面,便于维护和查找)。

应用自定义启用注解

然后,在任何需要使用SMS功能的Spring Boot应用中,你只需在配置类上加上@EnableSms注解。这样,SmsAutoConfig中定义的beans就会被自动注册到Spring上下文中。

@SpringBootApplication
@EnableSms // 启用SMS功能
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
1
2
3
4
5
6
7

通过自定义注解实现的半自动配置

使用自定义注解(如@EnableSms)配合@Import来引入自动配置类,则需要在应用中显式地通过添加该注解来启用特定的配置。这种方式提供了更多的控制,因为它允许开发者决定是否以及在哪里启用这些配置。

# 8. 打包安装

在自定义Spring Boot starter项目中进行打包时,一个重要的注意点是Spring Boot应用打包成的JAR文件是一个可执行的JAR(uber-jar),它包含了BOOT-INF目录用于存放应用的类和依赖。这种打包方式可能导致当这个JAR被其他项目作为依赖引入时,这些项目无法直接访问到BOOT-INF内的类。为了解决这个问题,你可以在pom.xml文件中对spring-boot-maven-plugin插件进行配置,使得在打包时生成一个额外的“非执行”JAR文件,该文件适用于作为依赖被其他项目引用

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <!-- 设置classifier为exec,这将在原有的可执行JAR之外,额外生成一个带有exec分类器的JAR文件。 -->
        <!-- 这个额外的JAR文件不包含用于运行Spring Boot应用的特定结构(如BOOT-INF目录), -->
        <!-- 从而使得它可以被其他项目作为依赖库正常引用。 -->
        <classifier>exec</classifier>
    </configuration>
</plugin>
1
2
3
4
5
6
7
8
9
10

如果在本地测试starter,直接执行install命令安装到本地仓库即可。

# 9. 在其他项目中的应用

9.1.首先在其他项目的pom.xml中引入相关依赖:

image-20240329045830144

<dependency>
    <groupId>com.scholar</groupId>
    <artifactId>scholar-spring-boot-starter</artifactId>
    <version>1.0</version>
</dependency>
1
2
3
4
5

9.2.在application.yml文件中添加配置

zzcloud:
  sms:
    access-key-id: 1314   
    access-key-secret: scholar
1
2
3
4

为了展示效果,我们写一个测试类,看能否看到我们想要的效果,测试代码如下:

@SpringBootTest
class SpringBootStarterTestApplicationTests {

	@Resource
	private ISmsService iSmsService;

	@Test
	void test() {
		iSmsService.send("55434818","书生","关注微信公众号:书生带你学编程", String.valueOf(new Date()));
	}

}
1
2
3
4
5
6
7
8
9
10
11
12

控制台效果如下:

image-20240329050531793

如果控制台有如上效果,说明我们整个自定义starter的过程就成功了!

# 案例二:AOP方式统一服务日志

原先实现统一日志都是放到每个工程中以AOP方式实现,现在有了starter方式,就可以将公司的日志规范集中到这里来统一管理。

这里我们使用之前创建好的项目进行案例二的编写,步骤与上同理:

# 1. 导入aop相关依赖

<!--aop相关依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1
2
3
4
5

# 2. 编写相关属性类(XxxProperties):WebLogProperties.java

package com.zking.zzcloudspringbootstarter.sms;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.io.Serializable;

/**
 * 专用于存储web日志相关配置的属性类。
 * 通过@ConfigurationProperties注解,该类可以绑定配置文件中以"zzcloud.weblog"为前缀的属性。
 */
@ConfigurationProperties("zzcloud.weblog")
public class WebLogProperties implements Serializable {
 
    // 用于控制是否启用web日志功能的属性。通过配置文件可进行设置。
    // Boolean类型允许这个属性在配置文件中不被设置时保持null,提供了更灵活的配置选择,例如默认不启用但允许通过配置启用。
    public Boolean enabled;
 
    /**
     * 获取是否启用web日志功能的属性值。
     *
     * @return 当前配置的是否启用web日志的布尔值。
     */
    public Boolean getEnabled() {
        return enabled;
    }
 
    /**
     * 设置是否启用web日志功能的属性值。
     * 
     * @param enabled 布尔值,指示是否启用web日志功能。
     */
    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 3. 编写Starter项目的业务功能

package com.zking.zzcloudspringbootstarter.weblog;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * 使用AOP(面向切面编程)技术实现的web日志记录切面。
 */
@Aspect // 表明这是一个切面类
@Component
@Slf4j 
public class WebLogAspect {
    
    /**
     * 定义切入点,指定哪些方法的执行需要被拦截记录日志。
     * 这里的切入点表达式指定了所有Controller下的所有方法。
     */
    @Pointcut("execution(* *..*Controller.*(..))")
    public void webLog(){}
 
    /**
     * 在切入点前执行的方法,用于记录请求到达前的相关信息。
     * 
     * @param joinPoint 连接点信息,提供了目标方法的详细信息。
     * @throws Throwable 可能抛出的异常。
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 从RequestContextHolder中获取请求的信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
 
        // 记录请求到达时的相关信息,包括请求URL、客户端IP地址和请求参数。
        log.info("开始服务:{}", request.getRequestURL().toString());
        log.info("客户端IP :{}", request.getRemoteAddr());
        log.info("参数值 :{}", Arrays.toString(joinPoint.getArgs()));
    }
 
    /**
     * 在切入点返回后执行的方法,用于记录返回的信息。
     * 
     * @param ret 目标方法执行完成后的返回值。
     * @throws Throwable 可能抛出的异常。
     */
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 记录请求处理完成后的返回值
        log.info("返回值 : {}", ret);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 4. 编写自动配置类AutoConfig

4.1@ConditionalOnProperty(prefix = "zzcloud.weblog",value = "enabled", matchIfMissing = true):

  • prefix和value:一起定义了需要检查的具体属性。例如,prefix = "zzcloud.weblog"和value = "enabled"一起指向zzcloud.weblog.enabled这个属性。
  • matchIfMissing:指定了当检查的属性缺失时,配置是否应该生效。true表示如果没有提供zzcloud.weblog.enabled属性,自动配置还是会默认生效。

4.2 @ConditionalOnMissingBean:

这个注解用于仅在Spring上下文中缺少指定类型的Bean时,才创建一个Bean。它确保了如果上下文中已经存在了一个相同类型的Bean,配置中的Bean定义将不会被加载,从而避免了Bean的重复定义。

package com.zking.zzcloudspringbootstarter.config;

import com.zking.zzcloudspringbootstarter.weblog.WebLogAspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Web日志的自动配置类。
 * 该类负责在满足特定条件下自动配置web日志相关的Bean。
 */
@Configuration // 标记为配置类,Spring Boot将自动扫描并加载该类的配置信息
@EnableConfigurationProperties({WebLogProperties.class}) // 启用WebLogProperties类的@ConfigurationProperties注解,使其可以从配置文件中绑定属性
@ConditionalOnProperty(prefix = "zzcloud.weblog", value = "enabled", matchIfMissing = true) // 条件注解,仅当zzcloud.weblog.enabled属性为true或未设置时,该配置类才有效
public class WebLogAutoConfig {
 
    /**
     * 定义web日志切面的Bean。
     * 只有当应用上下文中不存在WebLogAspect类型的Bean时,才会创建一个新的WebLogAspect Bean。
     * 这避免了Bean的重复定义。
     *
     * @return WebLogAspect Bean实例。
     */
    @Bean
    @ConditionalOnMissingBean // 在上下文中不存在WebLogAspect Bean时,才创建WebLogAspect Bean
    public WebLogAspect webLogAspect(){
        return new WebLogAspect(); // 创建并返回WebLogAspect实例
    }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 5. 编写spring.factories文件加载自动配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.zking.zzcloudspringbootstarter.config.SmsAutoConfig,\
  com.zking.zzcloudspringbootstarter.config.WebLogAutoConfig 
1
2
3

# 6. 打包,操作同上

# 7.在其他项目中引用

7.1.导入依赖,由于这里我们使用的是之前的那个项目,这里就不需要重复导入依赖了

7.2.在application.yml文件中添加配置

zzcloud:
  sms:
    access-key-id: 1314
    access-key-secret: scholar
  weblog:
    enabled: true # 开启日志
1
2
3
4
5
6

image-20240329052440545

如果我使用日志,就会拿到我请求的路径、我的客户端以及我的参数值,效果如下:

image-20240329052551571

如果由以上效果,说明我的案例二:AOP方式统一服务日志就成功了!

编辑此页 (opens new window)
上次更新: 2025/03/31, 02:05:41
Spring Boot - 自动配置
Spring Boot - 配置文件

← Spring Boot - 自动配置 Spring Boot - 配置文件→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式