大家好,这里是 盛码笔记

本篇我们来聊一聊 Spring Boot 的“魔法”是如何实现的。你可能已经用过 Spring Boot 快速搭建项目,但有没有想过:为什么只需要引入几个 starterSpring Boot 就能自动配置好整个应用环境?

这背后的核心机制就是:起步依赖(Starter Dependencies) 和 自动装配(Auto-Configuration)。本文将从源码角度带你深入理解这两个机制的底层原理,建议你具备一定的 Spring 基础后再阅读。

(一)起步依赖

依赖传递机制(Maven/Gradle)

依赖传递的话,在讲maven的时候有提到过,下面是地址(这里就不展开了)

Maven详细教程(包含安装)-CSDN博客

  • 核心作用:Starter 本身是一个空项目,仅包含一个 pom.xml(或 Gradle 构建文件),通过 Maven 的依赖传递聚合了某个场景所需的全部依赖。

  • 示例spring-boot-starter-webpom.xml 中声明了:

    <dependencies><dependency>spring-boot-starter</dependency><dependency>spring-boot-starter-json</dependency><dependency>spring-webmvc</dependency><dependency>spring-boot-starter-tomcat</dependency>
    </dependencies>
    
  • 效果:用户只需引入 spring-boot-starter-web,即可自动获得 Web 开发所需的所有库(Tomcat、Jackson、Spring MVC 等)。

(二)自动配置

  • 原理: Spring Boot 在启动时扫描 classpath,根据存在的类、Bean 定义以及属性设置,自动配置 Spring 应用所需的各种组件(如 DataSource, JPA, MVC, Security 等)。
  • 实现:
    • @EnableAutoConfiguration 注解(通常由 @SpringBootApplication 包含)启用自动配置。
    • 大量的 XxxAutoConfiguration 类(在 spring-boot-autoconfigure jar 中),这些类使用 @Configuration 定义配置,并使用 @ConditionalOnXxx(条件注解)来决定配置是否生效(例如 @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty)。

Bean的导入机制

1. @ComponentScan

作用
自动扫描指定包及其子包下所有被 @Component@Service@Controller@Repository 等注解标记的类,并注册为Bean。

特点

  • 基于包路径扫描:通过 basePackagesbasePackageClasses 指定扫描范围。
  • 批量注册:一次性注册多个Bean。
  • 依赖组件注解:目标类必须被Spring的组件注解标记(如 @Component)。
  • 过滤规则:支持通过 includeFilters/excludeFilters 自定义扫描规则。
@Configuration
@ComponentScan(basePackages = "com.example.service",includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Service")
)
public class AppConfig {}

适用场景
项目中自定义组件的批量注册(如Service、Controller层)。


2. @Import

作用
显式导入一个或多个配置类(@Configuration)、普通类(视为Bean)或 ImportSelector/ImportBeanDefinitionRegistrar 的实现类。

特点

  • 精确导入:直接指定要导入的类(而非包扫描)。
  • 无需组件注解:被导入的类不需要@Component 标记。
  • 支持多种类型
    • 配置类:导入其他 @Configuration 类。
    • 普通类:直接作为Bean注册。
    • ImportSelector:动态选择导入类。
    • ImportBeanDefinitionRegistrar:编程式注册Bean。
@Configuration
@Import({ DataSourceConfig.class,   // 导入配置类MyUtility.class,          // 普通类(注册为Bean)MyImportSelector.class    // 动态选择导入
})
public class AppConfig {}

适用场景

  • 导入第三方库的配置类(如 DataSourceConfig)。
  • 注册未被组件注解标记的类(如工具类)。

3. ImportSelector 接口

作用
通过编程方式动态选择要导入的配置类或Bean类,返回类的全限定名字符串数组。

特点

  • 动态决策:根据运行时条件(如配置参数、环境变量)决定导入哪些类。
  • 解耦设计:将导入逻辑从 @Import 注解中分离。
  • 无侵入性:目标类无需实现任何接口或注解。

实现步骤

  1. 实现 ImportSelector 接口。
  2. 重写 selectImports() 方法,返回要导入的类的全限定名。
  3. 通过 @Import 引入该 ImportSelector
public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {// 根据当前的运行环境动态选择配置文件(dev和prod环境)if (System.getenv("env").equals("prod")) {return new String[]{"com.example.ProdDataSourceConfig"};}else {return new String[] { "com.example.DevDataSourceConfig" };}}
}// 通过@Import引入
@Configuration
@Import(MyImportSelector.class)
public class AppConfig {}

适用场景
需要根据条件动态注册Bean(如环境区分、特性开关)。

4. @Bean 方法

@Configuration 类中使用 @Bean 注解的方法:

@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {return new HikariDataSource();}
}

特点

  • 适用于实例化过程复杂的 Bean(如连接池)
  • 可显式控制 Bean 的初始化逻辑
5. ImportBeanDefinitionRegistrar(了解)

编程式注册 BeanDefinition:

public class CustomRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {registry.registerBeanDefinition("customBean", new RootBeanDefinition(CustomBean.class));}
}

特点

  • 最底层的 Bean 注册方式
  • 可操作 BeanDefinition 元数据(作用域/Lazy/初始化方法等)

源码剖析

springboot版本为3.4.7

  1. 通过@SpringBootApplication是入口,点击进去。

    通过下面的源码可以发现,知道了为什么@SpringBootApplication扫描、注册当前包及子包下的Bean了,还可以当配置类使用。

    // 注解使用范围(类、接口或枚举类型)
    @Target({ElementType.TYPE})   
    // 保留策略:注解信息保留在运行时
    @Retention(RetentionPolicy.RUNTIME)
    // 注解会被包含在 JavaDoc 中
    @Documented
    // 继承:一个类使用了该注解,其子类将自动继承该注解
    @Inherited// 这个注解相当于@Configuration的增强版(配置类)
    @SpringBootConfiguration
    // 自动配置的核心注解(启用 Spring Boot 的自动配置机制)
    @EnableAutoConfiguration//  启用 Spring 的组件扫描功能,并通过自定义过滤器排除某些类不被扫描到容器中
    // 不指定包路径,默认扫描、注册当前包及子包下的Bean
    @ComponentScan(excludeFilters = {@Filter(				// 排除过滤器type = FilterType.CUSTOM,				// 表示使用开发者自定义的过滤器逻辑classes = {TypeExcludeFilter.class}		// 指定具体的过滤器类
    ), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}   // 排除那些已经被标记为“自动配置”的类
    )}
    )
    public @interface SpringBootApplication {// 这里省略了
    }
    

    再看一下@SpringBootConfiguration里面,下文可知,@SpringBootConfiguration的功能相当于增强版的@Configuration

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    // 配置类注解的功能
    @Configuration
    // 提升性能,支持编译时索引,加快组件扫描
    @Indexed
    public @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;
    }
    
  2. 再点击@EnableAutoConfiguration,进去继续看

    Spring Boot 自动装配的核心是 @EnableAutoConfigurationAutoConfigurationImportSelector(实现 ImportSelector

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    // 将主配置类(即标注了 @EnableAutoConfiguration 的类)所在的包作为自动扫描的根包。
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
    }
    

    然后是@AutoConfigurationPackage,它的作用是将主配置类所在的包作为自动扫描的根包。(至于后续逻辑就不展开了)

    在 Spring Boot 自动配置过程中,Spring 需要知道哪些包是“主应用包”,即:

    • 包含主配置类的包;
    • 需要被自动扫描和处理的包;

    这些信息会被存储在一个特殊的 Bean 中,例如通过 AutoConfigurationPackages.register(...) 注册到容器中。

    其他自动配置类(如 DataSourceAutoConfiguration)会依赖这些包路径来判断是否启用某些自动配置逻辑。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    // 从使用 @AutoConfigurationPackage 注解的类中读取 basePackages 和 basePackageClasses 属性;
    // 将这些包路径注册到 Spring 容器中;
    // 供后续 Spring Boot 自动配置机制使用。
    @Import({AutoConfigurationPackages.Registrar.class})
    public @interface AutoConfigurationPackage {String[] basePackages() default {};Class<?>[] basePackageClasses() default {};
    }
    
  3. 接下来需要点进入AutoConfigurationImportSelector类里面

    • 该类实现了DeferredImportSelector接口,而DeferredImportSelector接口继承于ImportSelector接口,所以实现了selectImports方法。这里与上文第三种导入Bean的方法类似,需要一个字符串数组。
    • 这里不清楚的就是getAutoConfigurationEntry,跳转过去
    // 代码部分省略
    /*** 根据注解元数据选择需要导入的自动配置类。** 该方法通常用于 Spring Boot 的自动配置机制中,根据当前类上的注解信息* 决定应该导入哪些自动配置类(即返回对应的全限定类名数组)。** @param annotationMetadata 注解元数据,描述了当前类上使用的注解信息* @return 返回一个字符串数组,包含所有需要导入的自动配置类的全限定名*/
    public String[] selectImports(AnnotationMetadata annotationMetadata) {// 如果自动配置未启用,则直接返回空数组,不导入任何配置类if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {// 获取自动配置条目(包含要导入的配置类集合)AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);// 将配置类集合转换为字符串数组并返回return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
    }
    

    点击getAutoConfigurationEntry方法跳转过去

    • 根据该方法的名称和返回值可以初步推测该方法时返回一个自动配置的对象
    • 该方法围绕configurations这个字符串列表集合,需要知道它是什么就再点击getCandidateConfigurations过去
    /*** 获取自动配置条目(包含要导入的自动配置类和被排除的类)** 该方法主要完成以下工作:* 1. 检查是否启用自动配置* 2. 获取注解属性* 3. 加载所有候选的自动配置类* 4. 去重处理* 5. 处理排除类* 6. 过滤不符合条件的配置类* 7. 发布自动配置导入事件** @param annotationMetadata 注解元数据,描述当前类上的注解信息* @return 返回一个 AutoConfigurationEntry 对象,包含自动配置类和排除类*/
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {// 1. 判断是否启用了自动配置,如果没有启用,则返回一个空的 AutoConfigurationEntryif (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {// 获取注解属性(例如 @EnableAutoConfiguration 上的 exclude 或 excludeName 属性)AnnotationAttributes attributes = this.getAttributes(annotationMetadata);// 获取所有的候选自动配置类(从 META-INF/spring.factories 中读取)List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//去除重复的配置类configurations = this.removeDuplicates(configurations);//获取需要排除的自动配置类(通过注解属性 exclude / excludeName)Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);//  从候选配置类中移除被排除的类configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);// 将处理后的配置类和排除类封装成 AutoConfigurationEntry 返回return new AutoConfigurationEntry(configurations, exclusions);}
    }
    

    点击getCandidateConfigurations过去

    在这个方法里面可以知道这是从某个路径中加载配置类,并返回字符串集合。

    /*** 加载所有候选的自动配置类*/
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 从 META-INF/spring/...imports 文件中加载自动配置类ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());// 获取配置类列表List<String> configurations = importCandidates.getCandidates();// 校验是否找到配置类,避免为空Assert.state(!CollectionUtils.isEmpty(configurations), "No auto configuration classes found in META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you are using a custom packaging, make sure that file is correct.");return configurations;
    }
    
  4. 获取加载配置类的全路径(包名+类名)

    采用debug断点调试

    • getCandidateConfigurations方法里面打一个断点
    • 然后运行主类即可

    可以发现this.autoConfigurationAnnotation.getName()获取的值是org.springframework.boot.autoconfigure.AutoConfiguration

    结合上面代码中长字符串的拼接加载配置类的完整路径是

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

    在这里插入图片描述

  5. 查看配置类

    怎么知道这个路径对不对呢?直接找出来看一下就知道了呗

    项目左边打开外部库,根据这个路径找对应的文件

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

    在这里插入图片描述
    打开文件可以发现全都是全类名(包名+类名),再结合上面讲Bean导入机制时,实现ImportSelector 接口的配置类可以全类名字符串数组加载、注册Bean。

    在这里插入图片描述

  6. 随便点击进入一个配置类,以GsonAutoConfiguration为例

    这里有代码创建了Gson的Bean实例,所以很多类不需要手动声明便可以直接使用,然后有一些以Conditional开头的注解作用是按条件加载对应的Bean,后面会详细讲解。

    在这里插入图片描述

按条件装配

  1. @ConditionalOnClass

    • 作用: 当且仅当项目的类路径 (classpath) 上存在指定的类时,才会加载被注解标记的配置(配置类、@Bean 方法、@Component 等)。

    • 场景:

      • 自动配置的核心: Spring Boot Starter 的自动配置类大量使用此注解。例如,DataSourceAutoConfiguration 可能这样

        @Configuration
        @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
        public class DataSourceAutoConfiguration {// ... 只有在类路径上有 DataSource 和 EmbeddedDatabaseType 时才配置数据源
        }
        

        这确保了只有当你引入了数据库驱动(如 HikariCP, tomcat-jdbc)等依赖(它们会提供 DataSource 实现类)时,Spring Boot 才会尝试自动配置数据源。避免了因为缺少依赖而抛出 ClassNotFoundException

      • 可选功能: 当你开发一个库或 Starter,并且某些功能需要特定的第三方库支持时,可以使用 @ConditionalOnClass 来确保只有用户引入了该库,你的功能才会被激活。

    • 注意事项: 指定的类名必须是全限定名com.example.SomeClass)。Spring Boot 通常使用 name 属性指定字符串形式的类名(避免在编译时就需要该类存在),但也支持直接使用 Class<?>[] value()

  2. @ConditionalOnMissingClass

    • 作用: 当且仅当项目的类路径 (classpath)不存在指定的类时,才会加载被注解标记的配置。

    • 场景:

      • 提供默认实现或回退方案: 当某个功能有多个实现(如日志框架:Logback, Log4j2),你可以为其中一个实现(如 Logback)配置默认 Bean,但条件设置为类路径上不存在另一个实现(如 Log4j2)的关键类。

        @Configuration
        @ConditionalOnMissingClass("org.apache.logging.log4j.core.LoggerContext") // 假设 Log4j2 的核心类
        public class DefaultLoggingConfiguration {@Beanpublic Logger myLogger() {// 配置默认的 Logback Logger}
        }
        

        这样,如果用户引入了 Log4j2,这个默认的 Logback 配置就不会生效。

      • 环境隔离: 在特定环境(如测试)下排除某些不需要的配置。

    • 注意事项: 同样需要指定全限定名。使用相对较少,因为 @ConditionalOnClass@ConditionalOnBean/@ConditionalOnMissingBean 组合通常能覆盖大部分需求。

  3. @ConditionalOnBean

    • 作用: 当且仅当 Spring 应用上下文 (IoC 容器)已经存在(已经定义或即将定义)指定类型、指定名称或满足指定条件的 Bean 时,才会加载被注解标记的配置。

    • 场景:

      • 依赖其他 Bean 的自动配置: 某个自动配置需要另一个 Bean 已经存在才能工作。例如,一个配置 JdbcTemplate 的自动配置类可能依赖于 DataSource Bean:

        @Configuration
        public class JdbcTemplateAutoConfiguration {@Bean@ConditionalOnBean(DataSource.class) // 只有容器中存在 DataSource Bean 时才创建 JdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}
        }
        

        确保 JdbcTemplate 只在有可用的 DataSource 时才被创建。

      • 按需配置扩展功能: 如果用户配置了某个特定的 Bean(如自定义的 MySpecialService),则自动激活与之相关的额外功能或配置。

    • 注意事项:

      • 匹配条件可以指定 Bean 的类型 (Class<?>)、名称 (String name) 或使用 @ConditionalOnSingleCandidate 等注解进一步限定。
      • Bean 定义顺序很重要! 被依赖的 Bean(DataSource)必须在依赖它的 Bean(JdbcTemplate之前被定义(或至少在同一批处理中能被检测到)。Spring Boot 的自动配置机制通过精心设计的加载顺序来解决这个问题。在自定义配置时需要注意潜在的循环依赖或顺序问题。
  4. @ConditionalOnMissingBean

    • 作用: 当且仅当 Spring 应用上下文 (IoC 容器)不存在(没有定义,也没有即将定义)指定类型、指定名称或满足指定条件的 Bean 时,才会加载被注解标记的配置。

    • 场景:

      • 提供默认 Bean 实现 (关键!): 这是 Spring Boot 自动配置的核心机制,也是用户覆盖默认配置的关键入口。自动配置类会使用此注解定义默认 Bean:

        @Configuration
        public class DataSourceConfiguration {@Bean@ConditionalOnMissingBean // 关键注解!如果用户没有自定义 DataSource Bean...public DataSource dataSource() {// ...则 Spring Boot 自动创建一个默认的 DataSource (如 HikariCP)return new HikariDataSource();}
        }
        
      • 用户自定义覆盖: 如果用户在自己的配置类中定义了相同类型的 Bean:

        @Configuration
        public class MyCustomConfig {@Beanpublic DataSource myCustomDataSource() { // 自定义 DataSourcereturn new MySpecialDataSource();}
        }
        

        此时,容器中已经存在一个类型为 DataSource 的 Bean (myCustomDataSource)。因此,自动配置类中带有 @ConditionalOnMissingBeandataSource() 方法将不会执行,用户自定义的 DataSource 生效。这就是用户覆盖 Spring Boot 默认配置的方式。

      • 避免重复定义: 确保只有在用户没有提供特定 Bean 时才提供默认实现。

总结

  1. 启动入口与 @SpringBootApplication
    • 应用启动始于标注了 @SpringBootApplication 的主类。
    • @SpringBootApplication 是一个复合注解,核心包含:
      • @SpringBootConfiguration: 标记该类为配置类(本质上是 @Configuration)。
      • @ComponentScan: 扫描主类所在包及其子包下的 @Component, @Service, @Repository, @Controller 等注解的类,将其注册为 Bean。
      • @EnableAutoConfiguration: 这是触发自动配置的关键注解
  2. @EnableAutoConfiguration 的魔力
    • 这个注解利用 Spring Framework 的 @Import 机制导入了 AutoConfigurationImportSelector 类。
    • AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口,它的核心方法是 selectImports()
  3. 加载自动配置候选列表 (spring.factories / AutoConfiguration.imports)
    • AutoConfigurationImportSelector.selectImports() 方法的核心任务是从 classpath 下的特定位置加载所有可能应用的自动配置类的全限定名列表
    • 加载位置 (优先级从上到下):
      1. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Spring Boot 2.7+ 推荐方式,一行一个全类名)。
      2. META-INF/spring.factories (旧方式,但广泛支持,EnableAutoConfiguration key 下列出全类名)。(spring 3.x版本后没有了)
    • Spring Boot 在 spring-boot-autoconfigure jar 包的 META-INF 目录下提供了大量内置的自动配置类(如 DataSourceAutoConfiguration, WebMvcAutoConfiguration, JacksonAutoConfiguration 等)。
    • 第三方 starter 也会在自己的 jar 包中提供相应的 spring.factoriesAutoConfiguration.imports 文件来声明其自动配置类。
  4. 自动配置类的过滤与筛选 (条件注解 @Conditional)
    • 加载得到的候选自动配置类列表并不会全部生效。Spring Boot 的核心机制在于按需配置
    • 每个自动配置类上都标注了一个或多个 @Conditional 及其衍生注解(条件注解),用来判断这个配置类是否应该被加载、其内部的 Bean 是否应该被创建。
    • 关键条件注解举例:
      • @ConditionalOnClass: 当 classpath 下存在指定的类时才生效。
      • @ConditionalOnMissingClass: 当 classpath 下不存在指定的类时才生效。
      • @ConditionalOnBean: 当 Spring 容器中存在指定的 Bean 时才生效。
      • @ConditionalOnMissingBean: 当 Spring 容器中不存在指定的 Bean 时才生效(这是用户覆盖默认配置的关键)。
      • @ConditionalOnProperty: 当指定的配置属性具有特定值时才生效。
      • @ConditionalOnResource: 当 classpath 下存在指定的资源文件时才生效。
      • @ConditionalOnWebApplication / @ConditionalOnNotWebApplication: 根据应用是否是 Web 应用来决定。
      • @ConditionalOnJava: 根据运行时的 Java 版本决定。
    • 条件评估过程:
      • Spring Boot 在启动时(具体在 ConfigurationClassPostProcessor 处理配置类期间)会创建一个 ConditionEvaluator
      • 对于每个候选的自动配置类,ConditionEvaluator解析并评估其上的所有条件注解
      • 只有所有条件都满足的自动配置类,才会被真正导入 Spring 应用上下文,其内部的 @Bean 方法才有机会被执行。
  5. 自动配置类的执行与 Bean 注册
    • 通过条件评估筛选出来的自动配置类,会被当作标准的 Spring @Configuration 类处理。
    • Spring 容器会解析这些配置类:
      • 执行类内部的 @Bean 方法,将返回的对象注册为 Spring 容器中的 Bean。
      • 处理类上的其他注解(如 @ImportResource, @PropertySource 等)。
    • 自动配置类的作用:
      • 创建和配置应用运行所需的核心基础设施 Bean(如 DataSource, JdbcTemplate, DispatcherServlet, ObjectMapper, RestTemplateBuilder 等)。
      • 根据 classpath 存在的依赖和用户配置的属性,智能地配置这些 Bean 的行为和默认值。
      • 提供默认的属性绑定(通过 @EnableConfigurationProperties + @ConfigurationProperties)。
  6. 用户配置优先
    • 核心原则:自动配置是非侵入式的。用户定义的 Bean 总是优先于自动配置提供的默认 Bean
    • 这是通过 @ConditionalOnMissingBean 注解实现的。自动配置类在定义其默认 Bean 时,通常会加上 @ConditionalOnMissingBean
    • 如果用户在自己的 @Configuration 类中显式定义了一个相同类型的 Bean,那么这个用户定义的 Bean 会被注册到容器中。
    • 当自动配置类执行到定义该默认 Bean 的方法时,@ConditionalOnMissingBean 条件检测到容器中已存在该类型的 Bean(用户定义的),条件不满足,该方法不会执行,从而避免了注册默认 Bean。用户配置成功覆盖了自动配置。
  7. 属性配置 (application.properties/yml)
    • 自动配置类大量使用 @ConfigurationProperties 来绑定外部配置(主要是 application.propertiesapplication.yml 文件)。
    • 这些属性用于覆盖自动配置提供的默认值。例如,配置 spring.datasource.url, spring.datasource.username, spring.datasource.password 来覆盖 DataSourceAutoConfiguration 创建数据源时使用的默认值。

(三)自定义starter

自定义Starter是一种封装特定功能(如自动配置、依赖管理等)的便捷方式,让其他项目能快速集成。

在这里插入图片描述

自定义JWT Starter实现

这里以JWT为例,讲解如何实现jwt starter如何实现,通过实现自定义stater可以更好理解起步依赖和自动配置。我们需要实现一个jwt-spring-boot-starter自定义starter,在其他模块导入这个依赖就可以直接调用JwtUtils工具类里面的方法。

jwt-starter/
├── jwt-spring-boot-autoconfigure/  // 自动配置模块
│   ├── src/main/java
│   │   └── com/example/jwt
│   │       ├── JwtAutoConfiguration.java
│   │       ├── JwtProperties.java
│   │       └── JwtUtils.java
│   │ 
│   └── pom.xml
├── jwt-spring-boot-starter/        // Starter模块
│   └── pom.xml
  1. 创建jwt-spring-boot-autoconfigure模块,只留下pom.xml文件和scr文件,删除主启动类,跟上面结构一样(需要留下resource目录),并导入依赖

    <!-- jjwt 核心 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><!-- 实现包 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><!-- 使用 jackson 解析 JSON --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope></dependency>
    
  2. 创建jwt-spring-boot-starter模块,只留下pom.xml文件,再导入jwt-spring-boot-autoconfigure模块依赖

    <dependency><groupId>com.example</groupId><artifactId>jwt-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version>
    </dependency>
    
  3. jwt-spring-boot-autoconfigure模块创建JwtProperties配置类

    package com.example.jwt;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "jwt")
    public class JwtProperties {// 密钥(Base64编码)private String secret = "c2VjcmV0LWtleS1mb3Itand0LWF1dGgtc3RhcnRlci1leGFtcGxl";// 有效时间(秒),默认1小时private long expiration = 3600;// 签发者private String issuer = "jwt-starter";// 是否开启调试模式private boolean debug = false;// Getters and Setterspublic String getSecret() {return secret;}public void setSecret(String secret) {this.secret = secret;}public long getExpiration() {return expiration;}public void setExpiration(long expiration) {this.expiration = expiration;}public String getIssuer() {return issuer;}public void setIssuer(String issuer) {this.issuer = issuer;}public boolean isDebug() {return debug;}public void setDebug(boolean debug) {this.debug = debug;}
    }
    
  4. 然后创建JwtUtils工具类

    package com.example;import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import io.jsonwebtoken.io.Decoders;
    import io.jsonwebtoken.security.Keys;import java.security.Key;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;public class JwtUtils {private final JwtProperties jwtProperties;public JwtUtils(JwtProperties jwtProperties) {this.jwtProperties = jwtProperties;}// 生成JWT Tokenpublic String generateToken(String subject) {return generateToken(subject, new HashMap<>());}public String generateToken(String subject, Map<String, Object> claims) {long now = System.currentTimeMillis();return Jwts.builder().setClaims(claims).setSubject(subject).setIssuer(jwtProperties.getIssuer()).setIssuedAt(new Date(now)).setExpiration(new Date(now + jwtProperties.getExpiration() * 1000)).signWith(getSignKey(), SignatureAlgorithm.HS256).compact();}// 解析JWT Tokenpublic Claims parseToken(String token) {return Jwts.parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token).getBody();}// 验证Token是否有效public boolean validateToken(String token) {try {parseToken(token);return true;} catch (Exception e) {if (jwtProperties.isDebug()) {e.printStackTrace();}return false;}}// 从配置的密钥获取签名Keyprivate Key getSignKey() {byte[] keyBytes = Decoders.BASE64.decode(jwtProperties.getSecret());return Keys.hmacShaKeyFor(keyBytes);}// 获取Token中的主题(通常是用户ID)public String getSubjectFromToken(String token) {Claims claims = parseToken(token);return claims.getSubject();}// 获取Token中的自定义声明public Object getClaimFromToken(String token, String claimName) {Claims claims = parseToken(token);return claims.get(claimName);}
    }
    
  5. 创建JwtAutoConfiguration自动配置的配置类

    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;@Configuration
    // 启用并注册配置属性类
    // application.yml 或 application.properties 中的配置项自动绑定到一个 Java Bean 类中。
    @EnableConfigurationProperties(JwtProperties.class)
    public class JwtAutoConfiguration {// 当spring容器不存在这个bean时,才注入该Bean@ConditionalOnMissingBean@Beanpublic JwtUtils jwtUtils (JwtProperties jwtProperties){return new JwtUtils(jwtProperties);}
    }
    
  6. 创建目录文件src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports导入自己的JwtAutoConfiguration全路径

    com.example.JwtAutoConfiguration  
    
  7. 然后是测试,另外新建一个模块,导入我们刚才写好的依赖

    <dependency><groupId>com.example</groupId><artifactId>jwt-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version>
    </dependency>
    
    @SpringBootTest
    public class TestLogin {@Autowiredprivate JwtUtils jwtUtils;@Testvoid JwtStarterTest (){HashMap<String, Object> map = new HashMap<>();map.put("username","tom");map.put("password","123123");String jwt = jwtUtils.generateToken("0001",map);System.out.println(jwt);}
    }
    

最后这里可以看见jwt-spring-boot-starter生效了

在这里插入图片描述

通过本文的讲解,相信你已经对 Spring Boot 的两大核心机制 —— 起步依赖(Starter) 和 自动装配(Auto-Configuration) 有了更深入的理解。

Spring Boot 的“开箱即用”并非魔法,而是建立在 Spring 强大的容器管理和条件化配置基础上的工程智慧。理解这些底层原理,不仅能帮助你更好地使用 Spring Boot,还能在遇到复杂问题时更快地定位和解决。

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏并分享给更多需要的小伙伴。这里是 盛码笔记,我们下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构:栈(区间问题)

码蹄集OJ-小码哥的栈 #include<bits/stdc.h> using namespace std; #define int long long const int N1e67; struct MOOE {int ll,rr; }; stack<MOOE>st; signed main( ) {ios::sync_with_stdio(false);cin.tie(nullptr);int n;cin>>n;while(n--){int opt…

Vue 中 data、watch、created 和 methods

以下是 Vue 中 data、watch、created 和 methods 的详细解释&#xff0c;结合常见使用场景和示例&#xff1a;1. data&#xff1a;响应式数据容器 作用&#xff1a;定义组件的响应式数据&#xff08;状态&#xff09;&#xff0c;当数据变化时&#xff0c;视图自动更新。特点&a…

精密模具冷却孔内轮廓检测方法探究 —— 激光频率梳 3D 轮廓检测

引言精密模具冷却孔的内轮廓精度直接影响注塑成型效率与制品质量。冷却孔具有深径比大&#xff08;可达 25:1&#xff09;、结构复杂&#xff08;多为螺旋形或异形&#xff09;、表面质量要求高&#xff08;Ra≤0.2μm&#xff09;等特点&#xff0c;传统检测方法难以满足其高精…

Vue单文件组件与脚手架工程化开发

一、Vue与VueComponent原型关系解析1. 原型链关系图解在Vue中&#xff0c;组件实例(VueComponent)与Vue实例之间存在特殊的原型链关系&#xff1a;VueComponent.prototype.__proto__ Vue.prototype这种设计使得所有组件都能访问Vue原型上定义的方法和属性。2. 原理验证示例// …

架构设计之计算高性能——单体服务器高性能

架构设计之计算高性能——单体服务器高性能 高性能是每个程序员共同的追求&#xff0c;无论是开发系统&#xff0c;还是仅仅只是写一段脚本&#xff0c;都希望能够达到高性能的效果&#xff0c;而高性能又是软件系统设计中最复杂的一步。无论是开发千万级并发的电商系统&#…

Unity灯光面板环境设置

在Unity中&#xff0c;环境设置&#xff08;Environment Lighting&#xff09; 是灯光面板&#xff08;Lighting Window&#xff09;的核心功能之一&#xff0c;用于控制场景的全局光照效果&#xff0c;包括天空盒、环境光、反射和雾效等。这些设置直接影响场景的整体氛围和真实…

MySQL语句优化案例

1.案例in查询条件很慢其中in中共115个select id,detail_id,request,response,utime,ctime from response_detaill where detaill_id in (26371986, 26372242, 26371984, 26371990, 26400150, 26371988, 26371994, 26371992,26371998, 26371996, 26371970, 26371968, 2637197…

能行为监测算法:低成本下的高效管理

AI监控智慧公司管理&#xff1a;降本增效的实践与突破一、背景&#xff1a;经济压力下的管理转型需求在经济下行周期&#xff0c;企业面临人力成本攀升、管理效率低下、安全风险频发等多重挑战。传统监控依赖人工巡检&#xff0c;存在响应滞后、误判率高、数据孤岛等问题&#…

当前(2024-07-14)视频插帧(VFI)方向的 SOTA 基本被三篇顶会工作占据,按“精度-速度-感知质量”三条线总结如下,供你快速定位最新范式

当前&#xff08;2024-07-14&#xff09;视频插帧&#xff08;VFI&#xff09;方向的 SOTA 基本被三篇顶会工作占据&#xff0c;按“精度-速度-感知质量”三条线总结如下&#xff0c;供你快速定位最新范式。感知质量最佳&#xff1a;CVPR 2024 ‑ PerVFI • 关键词&#xff1a;…

开源 python 应用 开发(七)数据可视化

最近有个项目需要做视觉自动化处理的工具&#xff0c;最后选用的软件为python&#xff0c;刚好这个机会进行系统学习。短时间学习&#xff0c;需要快速开发&#xff0c;所以记录要点步骤&#xff0c;防止忘记。 链接&#xff1a; 开源 python 应用 开发&#xff08;一&#xf…

基于深度学习的情感分析模型:从文本数据到模型部署

前言 情感分析&#xff08;Sentiment Analysis&#xff09;是自然语言处理&#xff08;NLP&#xff09;领域中的一个重要应用&#xff0c;它通过分析文本数据来判断文本的情感倾向&#xff0c;例如正面、负面或中性。随着社交媒体的兴起&#xff0c;情感分析在市场调研、品牌管…

使用python 实现一个http server

下面是一个使用 Python 内置库 http.server 的简单 HTTP 服务器实现。不需要安装任何第三方库&#xff0c;非常适合做演示或开发测试用。 from http.server import HTTPServer, BaseHTTPRequestHandlerclass SimpleHTTPRequestHandler(BaseHTTPRequestHandler):def do_GET(self…

Redis技术笔记-主从复制、哨兵与持久化实战指南

目录 前言 一、Redis主从复制 &#xff08;一&#xff09;Redis主从复制介绍 &#xff08;二&#xff09;基本环境准备 &#xff08;三&#xff09;工作原理 &#xff08;四&#xff09;结构模式 &#xff08;五&#xff09;一主一从&#xff08;无密码&#xff09; 配置…

sundog公司的SilverLining SDK库实现3d动态云层和下雨、下雨、雨夹雪效果

OSG系列文章目录 文章目录OSG系列文章目录前言一、3d动态云与下雨、下雪效果不能同时出现二、3d动态云与下雨、下雪效果不能同时出现的原因三、解决办法&#xff1a;前言 先看下效果&#xff1a;下雨 效果&#xff1a;下雪 效果&#xff1a;雨夹雪 &#x1f324;️ Sundo…

Python:简易的 TCP 服务端与客户端示例

下面是一个完整的 TCP 服务端与客户端示例&#xff0c;适用于 Python 3&#xff0c;使用 socket 模块&#xff0c;并正确处理了中文传输与异常情况&#xff0c;支持基本的多轮通信。TCP 服务端&#xff08;server_tcp.py&#xff09;import socket HOST 127.0.0.1 # 监听本地…

文心一言 4.5 开源深度剖析:中文霸主登场,开源引擎重塑大模型生态

> 百度用一场彻底的开源风暴,宣告中文大模型进入性能与普惠并重的新纪元——这里没有技术黑箱,只有开发者手中跃动的创新火花。 2025年,当全球大模型竞赛进入深水区,百度文心一言4.5的开源如同一颗重磅炸弹,彻底打破了“闭源即领先”的固有认知。这一次,中国团队不…

解决“Windows 无法启动服务”问题指南

错误1067&#xff1a;进程意外终止一、重启计算机有时系统出现临时性的服务故障&#xff0c;重启计算机就可以有效解决问题。需要注意的是&#xff0c;在重启之前&#xff0c;需要保存好所有未保存的工作&#xff0c;以免数据丢失。重启完成后&#xff0c;再次尝试启动相关服务…

银河麒麟(Kylin) - V10 GFB高级服务器操作系统ARM64部署昇腾910b训练机以及Docker安装

银河麒麟(Kylin) - V10 GFB高级服务器操作系统ARM64部署昇腾910b训练机以及Docker安装 原因 项目需要使用Deepseek-r1-distill-qwen-32b来做训练&#xff0c;在此记录 测试环境 服务器配置 型号&#xff1a;G5680V2 CPU&#xff1a;CPU 4Kunpeng 920-5250 NPU&#xff1a;NP…

消息中间件(Kafka VS RocketMQ)

目录 一、概要介绍 二、架构与原理 三、消费模式 1、Kafka—纯拉模式 2、RocketMQ—拉模式 3、RocketMQ—推模式 4、模式对比 四、特殊消息 1、顺序消息 2、消息过滤 3、延迟消息 4、事务消息 5、广播消息 五、高吞吐 六、高可用 七、高可靠 一、概要介绍 Apa…

MyBatis级联查询深度解析:一对多关联实战指南

MyBatis级联查询深度解析&#xff1a;一对多关联实战指南在实际企业级开发中&#xff0c;单表操作仅占20%的场景&#xff0c;而80%的业务需求涉及多表关联查询。本文将以一对多关系为例&#xff0c;深入剖析MyBatis级联查询的实现原理与最佳实践&#xff0c;助你掌握高效的数据…