当前位置: 首页 > news >正文

SpringBoot原理揭秘--自动装配

springboot的本质实际上是基于springFramework的二次封装,他的作用不是替代spring框架而是将spring框架的一些缺陷进行补足。springBoot则是提供了以下的几个功能

功能类别核心功能核心作用典型应用场景技术实现 / 关键组件
配置简化自动配置(Auto-configuration)根据依赖自动配置 Spring 组件,减少手动配置引入 Web 依赖后自动配置 MVC、Tomcat;引入数据库依赖后自动配置数据源@EnableAutoConfiguration 注解、spring.factories 配置文件、条件注解(@Conditional
起步依赖(Starters)预定义依赖集合,简化 Maven/Gradle 配置开发 Web 应用引入 spring-boot-starter-web;安全需求引入 spring-boot-starter-securityStarter 模块(如 spring-boot-starters)、依赖传递管理
部署简化嵌入式服务器内置服务器,无需单独部署 WAR 包直接通过 java -jar 运行应用;开发时无需手动启动外部 Tomcat内置 Tomcat(默认)、Jetty、Undertow,通过 spring-boot-starter-web 自动引入
外部化配置支持多环境配置,灵活调整参数开发 / 测试 / 生产环境使用不同数据库地址;动态修改服务器端口(server.portapplication.properties/yml、命令行参数、环境变量、配置中心(Spring Cloud Config)
生产支持Actuator 监控提供应用健康、指标、配置等监控能力检查应用存活状态(/actuator/health);监控 JVM 内存使用(/actuator/metricsspring-boot-starter-actuator 模块、端点(Endpoints)、Metrics 收集器
错误处理自动配置统一异常处理,简化错误页面和 JSON 响应Web 应用返回标准化错误信息;自定义 404/500 页面ErrorController 自动配置、@ControllerAdvice 全局异常处理
开发效率自动依赖管理统一管理依赖版本,避免版本冲突引入 Spring 生态组件(如 Spring Data、Spring Security)时无需手动指定版本号spring-boot-dependencies 父 POM、版本仲裁机制
测试支持集成主流测试框架,简化单元测试和集成测试测试 Controller 接口(@WebMvcTest);测试数据库交互(@DataJpaTestspring-boot-starter-test、JUnit 5、Mockito、Spring Test 切片测试注解
扩展能力自定义配置扩展允许覆盖自动配置,按需定制组件自定义数据源配置;替换默认 JSON 解析器(Jackson → Gson)@Configuration 注解、@Bean 定义组件、@Primary 优先注入
微服务集成无缝对接 Spring Cloud 生态,支持微服务架构服务注册与发现(Eureka)、负载均衡(Ribbon)、API 网关(Spring Cloud Gateway)Spring Cloud 组件(如 spring-cloud-starter-netflix-eureka-client
其他实用功能CLI 命令行工具通过 Groovy 脚本快速创建和运行应用快速原型开发;一行命令启动简单 Web 服务(spring run app.groovySpring Boot CLI 工具、Groovy 脚本支持
条件注解支持根据条件动态注册 Bean,增强配置灵活性仅在开发环境启用调试组件;当 Redis 依赖存在时注册 RedisTemplate@ConditionalOnClass@ConditionalOnProperty@ConditionalOnMissingBean 等注解

下面我们来挨个讲述

自动配置

springboot的重要特性之一就是“约定大于配置”,但是这个功能的实现是基于springboot的自动配置才能实现的。自动配置是基于springFramework配置组件装配的延伸。

组件装配的含义就是在springFramework中的ioc容器的bean对象需要从外部定义进行装配到容器内部

在原来的springFramework中往往是需要手动装配这些bean对象的:通过定义xml配置文件或者注解的方式来进行装配。springboot的出现使得哪些原来需要开发者一个个手动装配的bean转化成了框架自动根据项目场景来实现装配。而springBoot的自动装配的实现原理则是基于SpringFramework的模块装配和条件装配来实现的

模块装配:

模块装配则是springBoot实现的核心,它可以把一个模块中所需要的所有组件都加载到ioc容器当中。在SpringBoot中一般是引入大量的@Enablexxx系列注解来实现的

注解功能描述引入依赖 / 场景
@EnableAutoConfiguration启用 Spring Boot 的自动配置机制,根据 classpath 中的依赖自动配置 Bean。所有 Spring Boot 应用(通常通过 @SpringBootApplication 间接启用)
@EnableConfigurationProperties启用 @ConfigurationProperties 注解的类,将配置文件属性绑定到 Bean。需要读取 application.properties 的组件
@EnableAspectJAutoProxy启用 AOP 自动代理,支持 @Aspect@Before 等注解。需要切面编程的场景(如日志、事务)

什么是模块?

模块可以理解为一个个可以分解,组合,更换的独立单元。一个模块之间可以互相依赖,内部的功能是内聚的,主要目的完成一个功能

简单总结就是需要完成以下几个特性:

独立的,功能高内聚,可互相依赖,目标明确的

模块装配的核心在于:自定义注解+@import注解

自定义注解则是可以自定义一些类似@Enablexxx这种注解

@import注解用于导入其他配置类、组件或动态注册 Bean,是实现模块化配置的核心工具。@EnableXXX 注解通常通过 @Import 组合功能模块,简化配置。

// 使用注解启用功能
@SpringBootApplication
@EnableMyFeature
public class MyApp {// ...
}@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyFeatureConfiguration.class) // 导入配置类
public @interface EnableMyFeature {// 可定义注解属性
}@Configuration
public class MyFeatureConfiguration {@Beanpublic MyService myService() {return new MyService();}
}

import注解可以导入配置类,importSelector的实现类,ImportBeanDefinitionRegister的实现类以及普通类

配置类:

在使用import注解的时候可以在注解中添加配置类的信息,如下代码

// 数据库配置类
@Configuration
public class DatabaseConfig {@Beanpublic DataSource dataSource() {return new DriverManagerDataSource("jdbc:mysql://localhost:3306/test", "root", "password");}
}// 使用注解启用功能
@SpringBootApplication
@EnableMyFeature
public class MyApp {// ...
}@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(DatabaseConfig .class) // 导入配置类
public @interface EnableMyFeature {// 可定义注解属性
}

上述代码中导入了DatabaseConfig配置类信息,那么当使用EnableMyFeature的注解的时候就可以直接将DatabaseConfig配置类加载到ioc容器中,并且配置类内所定义的bean信息也会加载到ioc容器中去,上述代码的DataSource对象也会加入进去。

importSelector的实现类

ImportSelector是一个接口,是 Spring 框架中的一个核心接口,用于动态选择并导入需要注册为 Bean 的类。它在自动化配置、条件加载等场景中发挥关键作用,尤其在 Spring Boot 的自动配置机制中被广泛使用,对于import注解也可以加载ImportSelector的实现类。

public interface ImportSelector {String[] selectImports(AnnotationMetadata importingClassMetadata);
}

selectImports方法返回一个string类型的数组,这个数组内部则含有每个类的全量类名,表明一次性可以注入多个bean。

public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {try {// 检查 Redis 依赖是否存在Class.forName("org.springframework.data.redis.core.RedisTemplate");return new String[]{RedisConfig.class.getName()};} catch (ClassNotFoundException e) {return new String[]{InMemoryConfig.class.getName()};}}
}

上述代码则是返回了需要注入到ioc容器的bean的全量类路径。

掌握 ImportSelector 的使用,有助于理解 Spring Boot 自动配置的底层原理,并能自定义灵活的模块化配置方案。

ImportBeanDefinitionRegister的实现类

能够以编程的方式自定义注册 BeanDefinition,比ImportSelector拥有更强的灵活性

void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
对比项ImportSelectorImportBeanDefinitionRegistrar
导入方式返回类名,由 Spring 负责实例化直接注册 BeanDefinition
灵活性相对较弱,只能返回类名非常灵活,可以自定义 BeanDefinition
应用场景批量导入配置类动态注册 Bean,自定义 Bean 属性
执行时机在解析@Import注解时执行在所有 BeanDefinition 加载完成后执行

至于为什么ImportBeanDefinitionRegistrar相比ImportSelector更加灵活的原因就是ImportSelector返回类名信息之后是交给框架自己创建对象的,但是ImportBeanDefinitionRegistrar则是可以在代码中自定义创建对象,控制有关对象的属性信息。因此相比ImportSelector来说是更加灵活的方式。

同时ImportSelector会比ImportBeanDefinitionRegistrar更先执行,因为spring中读取注解的时机比spring加载BeanDefinition更加早。

条件装配

我们知道了模块装配,我们发现一个问题就是模块装配无法更加精细的控制在一些条件中不装配哪些组件。它比较适合哪些无需根据不同配置做出修改的模块,但是如果我们需要根据一些条件来决定是否要装配某些组件的时候就需要用到条件装配了。

profile的装配

profile装配是一种基于环境的配置;根据当前项目的不同运行时环境,可以动态的注册与当前运行环境匹配的组件

@Configuration
@Profile("dev")
public class DevConfig {@Beanpublic DataSource dataSource() {return DataSourceBuilder.create().url("jdbc:h2:mem:testdb").username("sa").password("password").build();}
}

上述代码中采用了profile注解,在profile注解中的dev则表示当前数据在何时的运行环境的时候才会注入当前的配置类,在默认的情况下系统只会注入profile值为default的数据,这表明当前代码的配置类在默认情况下不会注入ioc容器中,那么如何变更环境才能成功注入配置类信息呢?一般是通过以下几种

  • 命令行参数:在启动应用时添加参数,例如:java -jar app.jar --spring.profiles.active=dev
  • 环境变量:设置环境变量SPRING_PROFILES_ACTIVE=dev
  • 代码设置:在 Java 代码中通过ConfigurableEnvironment来设置
@Autowired
private ConfigurableEnvironment env;public void setActiveProfile() {env.setActiveProfiles("dev");
}

Conditional注解

  • 根据自定义条件决定是否加载 Bean。
  • Spring Boot 的自动配置核心依赖@Conditional系列注解(如@ConditionalOnClass@ConditionalOnProperty)。

使用方法

创建实现Condition接口的类:

public class MyCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 自定义判断逻辑,例如检查系统属性return System.getProperty("my.property") != null;}
}

使用@Conditional注解引用该条件:

@Bean
@Conditional(MyCondition.class)
public MyService myService() {return new MyService();
}
对比项@Conditional@Profile
本质通用条件注解,需自定义条件逻辑特殊条件注解,基于激活的 Profile
判断依据实现Condition接口的自定义逻辑当前激活的 Profile 名称
灵活性极高(可基于任意条件判断,如类、属性、环境)有限(仅基于 Profile 名称)
应用场景复杂条件判断(如类存在性、属性值、系统状态)环境隔离(开发 / 测试 / 生产环境配置切换)
注解位置可用于@Component@Bean@Configuration同上
执行时机Bean 定义加载前@Conditional类似
http://www.lryc.cn/news/603130.html

相关文章:

  • LeetCode 2044.统计按位或能得到最大值的子集数目:二进制枚举/DFS回溯(剪枝)
  • Leaflet 综合案例 - 路径规划
  • 3. 卷积网络代码参数解读分析
  • 前端高级综合搜索组件 SearchBox 使用详解!
  • 4.DRF 认证--Authentication4.DRF 认证--Authentication
  • django ManyToManyField 如何添加数据
  • python-内存管理
  • Kubernetes --存储入门
  • 基于FPGA和DDS原理的任意波形发生器(含仿真)
  • 做了一款小而美的本地校验器
  • 【0基础PS】PS工具详解--选择工具--对象选择工具
  • 天学网面试 —— 中级前端开发岗位
  • B树、B+树、红黑树区别
  • 安宝特案例丨AR+AI+SOP?3大技术融合革新军工航天领域
  • windows部署ACE-Step记录
  • 语音识别数据增强
  • Redis实战(3)-- 高级数据结构zset
  • C++现代Redis客户端库redis-plus-plus详解
  • 第四章:分析 Redis 性能高原因和核心字符串类型命令
  • 散点图(散点矩阵)相关介绍
  • 3. Socket 编程 TCP
  • 群晖Synology Drive:打造高效安全的私有云协作平台
  • TDengine 中 TDgpt 用于异常检测
  • 【51单片机2位数码管跑马灯】2022-9-25
  • 04动手学深度学习(下)
  • C++ 哈希算法、贪心算法
  • 【硬件】LVGL
  • 六轴机械臂cad【11张】三维图+设计说明书
  • 用latex+vscode+ctex写毕业论文
  • node后端-JWT认证