SpringBoot Starter的用法以及原理小结

 更新时间:2026年03月02日 09:54:21   作者:Nyarlathotep0113  
本文介绍了如何创建一个Spring Boot Starter项目,包括创建父模块、子模块以及自动配置类的编写,通过这种方式,可以将功能模块化,并实现自动装配,下面就来具体介绍一下

为了理解SpringBoot Starter的写法,以hello-spring-boot-starter作为示例来讲解

创建父模块hello-spring-boot-starter-project

hello-spring-boot-starter-project将作为整个项目的父模块,其pom.xml文件如下:

<!--添加子模块-->  
<modules>  
<module>hello-spring-boot-starter</module>  
<module>hello-spring-boot-starter-autoconfigure</module>  
</modules>  
<!--定义的参数-->  
<properties>  
<maven.compiler.source>17</maven.compiler.source>  
<maven.compiler.target>17</maven.compiler.target>  
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
<spring-boot.version>3.0.7</spring-boot.version>  
</properties>  
<!--依赖的配置约定-->  
<dependencyManagement>  
<dependencies>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-autoconfigure</artifactId>  
<version>${spring-boot.version}</version>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-configuration-processor</artifactId>  
<version>3.5.11</version>  
</dependency>  
</dependencies>  
</dependencyManagement> 

创建子模块hello-spring-boot-starter-autoconfigure

hello-spring-boot-starter-autoconfigure作为父模块hello-spring-boot-starter-project的一个子模块,其pom.xml关键配置如下:

<!--设置父模块-->  
<parent>  
<groupId>edu.whut</groupId>  
<artifactId>hello-spring-boot-starter-project</artifactId>  
<version>1.0-SNAPSHOT</version>  
</parent>
<dependencies>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-autoconfigure</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-configuration-processor</artifactId>  
</dependency>  
</dependencies>

在其模块内部创建三个文件
HelloService.java:

public class HelloService {  
private final HelloProperties properties;  
  
public HelloService(HelloProperties properties) {  
this.properties = properties;  
}  
  
public void sayHello() {  
System.out.println("Hello,"+properties.getObject());  
}  
}

HelloProperties.java:

@ConfigurationProperties(prefix = "hello")  
public class HelloProperties {  
private String object;  
  
public String getObject() {  
return object;  
}  
  
public void setObject(String object) {  
this.object = object;  
}  
}

HelloAutoConfiguration.java:

//标识自动配置类  
@AutoConfiguration  
@EnableConfigurationProperties(HelloProperties.class)  
public class HelloAutoConfiguration {  
@Bean  
//在application文件中配置了hello.object配置才会构造这个bean  
@ConditionalOnProperty(prefix = "hello", name = "object")  
public HelloService helloService(HelloProperties properties) {  
return new HelloService(properties);  
}  
}

hello-spring-boot-starter-autoconfigure模块中的resources下的META-INF下的spring目录下创建一个名为org.springframework.boot.autoconfigure.AutoConfiguration.imports的文件,文件中写入:

edu.whut.HelloAutoConfiguration

创建hello-spring-boot-starter子模块

hello-spring-boot-starter作为父模块hello-spring-boot-starter-project的一个子模块,其pom.xml关键配置如下:

<!--指定父模块-->  
<parent>  
<groupId>edu.whut</groupId>  
<artifactId>hello-spring-boot-starter-project</artifactId>  
<version>1.0-SNAPSHOT</version>  
</parent>
<dependencies>  
<dependency>  
<groupId>edu.whut</groupId>  
<artifactId>hello-spring-boot-starter-autoconfigure</artifactId>  
<version>1.0-SNAPSHOT</version>  
</dependency>  
</dependencies>

这样这个模块就完成了

使用hello-spring-boot-starter

随意创建一个springboot项目,在main方法中查找对应的名为helloServicebean

public static void main(String[] args) {  
ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);  
Object bean = applicationContext.getBean("helloService");  
System.out.println(bean);  
}

结果为:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'helloService' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:978)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1381)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1296)
    at com.example.demo.DemoApplication.main(DemoApplication.java:12)

这是正常的,因为现在还未设置配置,更改aplication.properties,写入:

hello.object="world"

再次运行程序,现在就可以获取到starter中配置的bean

edu.whut.HelloService@4463d9d3

为什么要使用springboot starter?

有人可能好奇,这么大费周章,就是为了把一个对象注入到IOC容器中?那为什么不直接在项目里注入?
因为每个Starter是一个高内聚的功能模块,通过依赖传递条件化配置@ConditionalOnClass等)实现“智能装配”,避免冗余代码。

为什么不直接写starter,反而需要一个autoconfigure?

这是为了遵循Spring Boot官方提倡的“关注点分离”原则,将自动配置逻辑和依赖管理解耦,让架构更清晰、更易维护。
autoconfigure包含条件化配置类、@ConfigurationProperties、META-INF/spring.factories(或org.springframework.boot.autoconfigure.AutoConfiguration.imports)

starter仅一个pom.xml,聚合autoconfigure模块​ + 该功能所需的所有第三方依赖

springboot starter自动装配的原理

@SpringBootApplciation注解是一个组合注解,这个注解被一个@EnableAutoConfiguration所注解。

@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 核心关键
public @interface EnableAutoConfiguration {
    // ...
}

ImportSelector是一个用于动态、编程式地选择要导入的配置类的核心接口,它是一个函数式接口,核心方法是:

String[] selectImports(AnnotationMetadata importingClassMetadata);

这个方法根据给定的注解元数据(被@Import注解的类的信息),返回一个由全限定类名组成的字符串数组。这些返回的类名会在运行时被Spring容器处理,就像它们原本就被@Import注解直接引用一样,其内部的@Configuration@Bean等注解会被正常解析。

AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了ImportSelector接口,实现了selectImports方法。

public String[] selectImports(AnnotationMetadata annotationMetadata) {  
// 1. 检查是否开启了自动装配(默认是开启的)
if (!this.isEnabled(annotationMetadata)) {  
return NO_IMPORTS;  
} else {  
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);  
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());  
}  
}

因此核心的逻辑是getAutoConfigurationEntry(annotationMetadata)

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {  
if (!this.isEnabled(annotationMetadata)) {  
return EMPTY_ENTRY;  
} else {  
AnnotationAttributes attributes = this.getAttributes(annotationMetadata); 
//加载所有候选配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);  //移除重复和显式排除的类
configurations = this.removeDuplicates(configurations);  
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);  
this.checkExcludedClasses(configurations, exclusions);  
configurations.removeAll(exclusions);  
//进行条件注解的筛选
configurations = this.getConfigurationClassFilter().filter(configurations);  
this.fireAutoConfigurationImportEvents(configurations, exclusions);  
return new AutoConfigurationEntry(configurations, exclusions);  
}  
}

在这个方法中,核心逻辑只有三步:

  1. 加载所有的候选配置类
  2. 移除重复和显式排除的类
  3. 进行“条件注解”筛选

加载候选配置类

调用 getCandidateConfigurations()。这个方法会去约定好的位置,读取一个文件,这个文件里列出了所有可能被加载的自动配置类。
这个约定好的位置,在springboot3是META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,这个文件的每一行是一个全类名,在springboot2.x中是META-INF/spring.factories,org.springframework.boot.autoconfigure.EnableAutoConfiguration为键的值就是一系列全类名。
无论哪种格式,此时得到的都是一个巨大的List,包含了Spring Boot所有内置的(如DataSourceAutoConfiguration, WebMvcAutoConfiguration)以及第三方starter提供的自动配置类。因此要进行后续的排除和筛选。

总结

  1. 启动:执行SpringApplication.run(),启动Spring容器
  2. 触发:容器解析主类上的@SpringBootApplication->@EnableAutoConfiguration
  3. 决策:@EnableAutoConfiguration导入AutoConfigurationImportSelector
  4. 扫描:AutoConfigurationImportSelector从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 3)中读取所有候选自动配置类。
  5. 排除和筛选:对候选列表进行层层筛选。
  6. 导入:将最终满足所有条件的自动配置类的全限定名数组返回给容器。
  7. 解析与注册:容器将这些自动配置类当作普通的 @Configuration类进行解析,将其内部符合条件的 @Bean方法注册为Bean定义。

到此这篇关于SpringBoot Starter的用法以及原理小结的文章就介绍到这了,更多相关SpringBoot Starter用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA如何导入已有Maven项目

    IDEA如何导入已有Maven项目

    导入Maven项目到IDEA时,经常遇到问题,本文记录正确步骤,首先创建空项目,然后选择Import Module from external model并选择Maven,勾选所需profiles和Maven项目,最后设置Project SDK,这样配置后,项目结构将符合预期,仅显示一个Module
    2024-11-11
  • Springboot项目基于Devtools实现热部署步骤详解

    Springboot项目基于Devtools实现热部署步骤详解

    这篇文章主要介绍了Springboot项目基于Devtools实现热部署,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • Spring注解之Service用法及示例详解

    Spring注解之Service用法及示例详解

    使用 @Service 注解可以将一个类声明为业务逻辑组件,并将其对象存入 Spring 容器中,在控制器类中,通过注入该组件的实例,即可调用其中的方法,这篇文章主要介绍了Spring注解之Service用法及示例详解,需要的朋友可以参考下
    2024-04-04
  • Java数组归纳总结

    Java数组归纳总结

    这篇文章主要介绍了Java数组归纳总结,总结内容有一维数组、二维数组、遍历数组、替换元素、数组排序、数组拷贝、元素查询、排序算法,下面来看看这些方法的相关资料,需要的小伙伴可以辛苦一下
    2022-01-01
  • springmvc组件中的HandlerMapping解析

    springmvc组件中的HandlerMapping解析

    这篇文章主要介绍了springmvc九大组件中的HandlerMapping解析,HandlerMapping表示的是一个URL与一个Handler(可以简单的理解为Controller中有@RequestMapping注解的方法)之间的映射关系,需要的朋友可以参考下
    2023-09-09
  • 一文详解Java中的可变对象(Mutable)与不可变对象(Immutable)

    一文详解Java中的可变对象(Mutable)与不可变对象(Immutable)

    如何在 Java 中创建不可变对象?我以前以为所有对象都是不可变的,因为如果你改变一个 String 实例的内容,它总是会创建一个新的 String 对象并指向该对象,在本文中,我不仅将分享在 Java 中Immutable的步骤,还将讨论可变对象与不可变对象及其优缺点
    2023-11-11
  • Spring Date jpa 获取最新一条数据的实例代码

    Spring Date jpa 获取最新一条数据的实例代码

    这篇文章主要介绍了Spring Date jpa 获取最新一条数据的实例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • SpringBoot集成Curator实现Zookeeper基本操作的代码示例

    SpringBoot集成Curator实现Zookeeper基本操作的代码示例

    Zookeeper是一个Apache开源的分布式的应用,为系统架构提供协调服务,ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户,本文给大家介绍了SpringBoot集成Curator实现Zookeeper基本操作,需要的朋友可以参考下
    2024-05-05
  • Java 实现定时任务的三种方法

    Java 实现定时任务的三种方法

    这篇文章主要介绍了Java 实现定时任务的三种方法,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • Java中的ReentrantLock原理解析

    Java中的ReentrantLock原理解析

    这篇文章主要介绍了Java中的ReentrantLock原理解析,ReentrantLock是Java中的一个线程同步工具,它提供了比synchronized更灵活和强大的功能。它是一个可重入的互斥锁,意味着同一个线程可以多次获取该锁,而不会发生死锁,需要的朋友可以参考下
    2023-11-11

最新评论