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-security | Starter 模块(如 spring-boot-starters )、依赖传递管理 | |
部署简化 | 嵌入式服务器 | 内置服务器,无需单独部署 WAR 包 | 直接通过 java -jar 运行应用;开发时无需手动启动外部 Tomcat | 内置 Tomcat(默认)、Jetty、Undertow,通过 spring-boot-starter-web 自动引入 |
外部化配置 | 支持多环境配置,灵活调整参数 | 开发 / 测试 / 生产环境使用不同数据库地址;动态修改服务器端口(server.port ) | application.properties /yml 、命令行参数、环境变量、配置中心(Spring Cloud Config) | |
生产支持 | Actuator 监控 | 提供应用健康、指标、配置等监控能力 | 检查应用存活状态(/actuator/health );监控 JVM 内存使用(/actuator/metrics ) | spring-boot-starter-actuator 模块、端点(Endpoints)、Metrics 收集器 |
错误处理自动配置 | 统一异常处理,简化错误页面和 JSON 响应 | Web 应用返回标准化错误信息;自定义 404/500 页面 | ErrorController 自动配置、@ControllerAdvice 全局异常处理 | |
开发效率 | 自动依赖管理 | 统一管理依赖版本,避免版本冲突 | 引入 Spring 生态组件(如 Spring Data、Spring Security)时无需手动指定版本号 | spring-boot-dependencies 父 POM、版本仲裁机制 |
测试支持 | 集成主流测试框架,简化单元测试和集成测试 | 测试 Controller 接口(@WebMvcTest );测试数据库交互(@DataJpaTest ) | spring-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.groovy ) | Spring 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);
对比项 | ImportSelector | ImportBeanDefinitionRegistrar |
---|---|---|
导入方式 | 返回类名,由 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 类似 |