网站首页 > 博客文章 正文
一、IoC基础概念与理解
1.1 什么是控制反转(IoC)
控制反转(Inversion of Control)是一种设计原则,用于将传统程序中的控制流程反转。在传统编程中,我们的代码直接调用依赖对象,而在IoC中,这个控制权被反转了——由外部容器(在Spring中就是IoC容器)来负责对象的创建和依赖注入。
生活化比喻: 想象你去餐厅点餐:
- 传统方式:你自己去厨房找食材、烹饪(主动获取依赖)
- IoC方式:你只需点菜,服务员会把做好的菜端给你(被动接收依赖)
1.2 Spring IoC容器的核心组件
组件 | 说明 | 类比 |
BeanFactory | IoC容器的基础接口,提供基本的DI功能 | 餐厅的基本厨房 |
ApplicationContext | BeanFactory的子接口,添加了更多企业级功能 | 高级餐厅,除了做菜还有音乐、装饰等服务 |
BeanDefinition | 描述bean的定义信息 | 菜谱,描述如何做一道菜 |
BeanPostProcessor | 对bean进行后处理 | 菜品装饰师,在上菜前进行最后装饰 |
BeanFactory
+getBean(name) : : Object
+containsBean(name) : : boolean
+isSingleton(name) : : boolean
ApplicationContext
+getEnvironment() : : Environment
+publishEvent(event) : : void
BeanDefinition
-beanClass: Class
-scope: String
-lazyInit: boolean
+getPropertyValues() : : PropertyValues
1.3 第一个Spring IoC示例
让我们创建一个最简单的Spring Boot应用来演示IoC:
// 1. 定义一个简单的服务类
@Service // @Service注解告诉Spring这是一个服务层的Bean
public class GreetingService {
public String greet(String name) {
return "Hello, " + name + "!";
}
}
// 2. 创建一个控制器类来使用这个服务
@RestController
public class GreetingController {
private final GreetingService greetingService;
// 3. 通过构造器注入依赖 - IoC的核心体现
@Autowired // @Autowired告诉Spring自动注入依赖
public GreetingController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/greet")
public String greet(@RequestParam String name) {
return greetingService.greet(name);
}
}
// 4. 主应用类
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
代码解析:
- @Service注解将GreetingService标记为Spring管理的Bean
- @RestController将控制器类标记为Spring MVC组件
- @Autowired实现了依赖的自动注入,这是IoC的核心体现
- Spring Boot应用启动时会自动扫描这些组件并建立依赖关系
二、Spring Bean的详细解析
2.1 Bean的作用域(Scope)
Spring Bean支持多种作用域,以下是常用作用域的对比:
作用域 | 说明 | 适用场景 | 生命周期 |
singleton | 默认作用域,每个容器只有一个实例 | 无状态服务、工具类 | 容器启动时创建,容器关闭时销毁 |
prototype | 每次请求都创建一个新实例 | 有状态对象、需要隔离的场景 | 每次获取时创建,不管理销毁 |
request | 每个HTTP请求创建一个实例 | Web应用中的请求相关数据 | 请求开始时创建,请求结束时销毁 |
session | 每个HTTP会话一个实例 | 用户会话数据 | 会话开始时创建,会话结束时销毁 |
application | ServletContext生命周期 | 全局应用数据 | Web应用启动时创建,应用关闭时销毁 |
// 作用域配置示例
@Configuration
public class ScopeConfig {
@Bean
@Scope("singleton") // 显式声明为单例,默认可以不写
public SingletonService singletonService() {
return new SingletonService();
}
@Bean
@Scope("prototype")
public PrototypeService prototypeService() {
return new PrototypeService();
}
}
2.2 Bean的生命周期
Spring Bean的生命周期包含多个阶段,可以通过实现特定接口或使用注解来干预:
实例化Bean
填充属性
调用Aware接口方法
BeanPostProcessor前置处理
初始化方法调用
BeanPostProcessor后置处理
Bean就绪可用
容器关闭
销毁方法调用
生命周期回调示例:
@Service
public class LifecycleService implements InitializingBean, DisposableBean {
public LifecycleService() {
System.out.println("1. 构造器调用");
}
@PostConstruct
public void postConstruct() {
System.out.println("3. @PostConstruct方法调用");
}
@Override
public void afterPropertiesSet() {
System.out.println("4. InitializingBean.afterPropertiesSet()调用");
}
@PreDestroy
public void preDestroy() {
System.out.println("6. @PreDestroy方法调用");
}
@Override
public void destroy() {
System.out.println("7. DisposableBean.destroy()调用");
}
}
// 输出顺序:
// 1. 构造器调用
// 2. 属性注入(如果有)
// 3. @PostConstruct方法调用
// 4. InitializingBean.afterPropertiesSet()调用
// 5. BeanPostProcessor处理
// ... (使用阶段)
// 6. @PreDestroy方法调用
// 7. DisposableBean.destroy()调用
2.3 多种依赖注入方式对比
Spring提供了多种依赖注入方式,各有优缺点:
注入方式 | 代码示例 | 优点 | 缺点 | 推荐场景 |
构造器注入 | public A(B b) {this.b=b;} | 不可变、完全初始化、易于测试 | 参数多时代码冗长 | 推荐的主要方式 |
Setter注入 | public void setB(B b) | 灵活、可选依赖 | 对象可能部分初始化 | 可选依赖 |
字段注入 | @Autowired private B b; | 简洁 | 不易测试、隐藏依赖 | 不推荐,仅简单原型 |
方法注入 | @Autowired public void setup(B b) | 灵活 | 不常用 | 特殊场景 |
构造器注入最佳实践:
@Service
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
// 单一构造器可省略@Autowired
public OrderService(PaymentService paymentService,
InventoryService inventoryService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}
// 业务方法...
}
三、高级IoC特性
3.1 条件化Bean注册
Spring提供了多种条件化注册Bean的方式:
@Configuration
public class ConditionalConfig {
// 只有当dev.properties存在时才注册
@Bean
@ConditionalOnResource(resources = "classpath:dev.properties")
public DevService devService() {
return new DevService();
}
// 只有当DataSource Bean存在时才注册
@Bean
@ConditionalOnBean(DataSource.class)
public DataSourceChecker dataSourceChecker() {
return new DataSourceChecker();
}
// 根据系统属性决定是否注册
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new SimpleCacheManager();
}
}
3.2 Bean的延迟初始化
@Configuration
public class LazyConfig {
@Bean
@Lazy // 延迟初始化,只有第一次使用时才创建
public HeavyResource heavyResource() {
// 模拟一个初始化很重的资源
System.out.println("初始化HeavyResource...");
return new HeavyResource();
}
@Bean
public ConsumerService consumerService() {
// 此时heavyResource不会被初始化
return new ConsumerService();
}
}
// 测试类
@SpringBootTest
public class LazyTest {
@Autowired
private ApplicationContext context;
@Test
public void testLazy() {
System.out.println("应用上下文已启动");
// 只有在这里才会初始化HeavyResource
HeavyResource resource = context.getBean(HeavyResource.class);
}
}
3.3 使用@Primary解决自动装配歧义
当有多个同类型Bean时,可以使用@Primary指定首选Bean:
public interface MessageService {
String getMessage();
}
@Service
@Primary // 当有多个MessageService时优先选择这个
class EmailService implements MessageService {
@Override
public String getMessage() {
return "Email message";
}
}
@Service
class SmsService implements MessageService {
@Override
public String getMessage() {
return "SMS message";
}
}
@Service
public class NotificationService {
// 因为有@Primary,这里会自动注入EmailService
@Autowired
private MessageService messageService;
public void send() {
System.out.println(messageService.getMessage());
}
}
四、IoC容器底层原理深度解析
4.1 Spring IoC容器工作流程
BeanDefinitionBeanFactorySpringContainerClientBeanDefinitionBeanFactorySpringContainerClientloop[对于每个Bean]启动应用上下文加载配置元数据解析为BeanDefinition实例化Bean填充属性处理Aware接口应用BeanPostProcessor前置处理调用初始化方法应用BeanPostProcessor后置处理返回完全配置的应用
4.2 BeanFactory与ApplicationContext对比
特性 | BeanFactory | ApplicationContext |
Bean实例化/装配 | 是 | 是 |
自动BeanPostProcessor注册 | 否 | 是 |
自动BeanFactoryPostProcessor注册 | 否 | 是 |
便捷的MessageSource访问 | 否 | 是 |
内置ApplicationEvent发布机制 | 否 | 是 |
启动速度 | 快 | 较慢 |
内存占用 | 少 | 较多 |
适用场景 | 资源受限环境 | 大多数应用 |
4.3 循环依赖解决方案
Spring通过三级缓存解决构造器注入无法解决的循环依赖问题:
// 循环依赖示例
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
// Spring解决循环依赖的三级缓存:
// 1. singletonObjects:存放完全初始化好的Bean
// 2. earlySingletonObjects:存放早期引用(原始对象)
// 3. singletonFactories:存放ObjectFactory,用于生成早期引用
解决流程:
- 创建ServiceA实例(未初始化) → 放入三级缓存
- ServiceA需要注入ServiceB → 开始创建ServiceB
- 创建ServiceB实例(未初始化) → 放入三级缓存
- ServiceB需要注入ServiceA → 从三级缓存获取ServiceA的早期引用
- ServiceB完成初始化 → 放入一级缓存
- ServiceA注入ServiceB完成初始化 → 放入一级缓存
五、实战:自定义IoC扩展
5.1 实现自定义BeanPostProcessor
// 自定义BeanPostProcessor,用于监控Bean初始化时间
@Component
public class TimingBeanPostProcessor implements BeanPostProcessor, Ordered {
private static final Logger log = LoggerFactory.getLogger(TimingBeanPostProcessor.class);
private final Map<String, Long> startTimes = new ConcurrentHashMap<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
startTimes.put(beanName, System.currentTimeMillis());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
Long startTime = startTimes.remove(beanName);
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
log.info("Bean '{}' 初始化耗时 {} ms", beanName, duration);
}
return bean;
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE; // 最后执行
}
}
// 应用输出示例:
// Bean 'myController' 初始化耗时 12 ms
// Bean 'myService' 初始化耗时 8 ms
5.2 自定义作用域实现
// 1. 实现自定义Scope(这里实现一个简单的线程作用域)
public class ThreadScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadScope =
ThreadLocal.withInitial(() -> new HashMap<>());
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = threadScope.get();
Object object = scope.get(name);
if (object == null) {
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
@Override
public Object remove(String name) {
Map<String, Object> scope = threadScope.get();
return scope.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
// 线程作用域通常不需要销毁回调
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}
// 2. 注册自定义Scope
@Configuration
public class ThreadScopeConfig implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerScope("thread", new ThreadScope());
}
}
// 3. 使用自定义Scope
@Component
@Scope("thread")
public class ThreadScopedBean {
private final int value = new Random().nextInt(100);
public int getValue() {
return value;
}
}
// 4. 测试
@RestController
public class ScopeTestController {
@Autowired
private ThreadScopedBean threadScopedBean;
@GetMapping("/thread")
public String test() {
return "ThreadScopedBean value: " + threadScopedBean.getValue() +
", Thread: " + Thread.currentThread().getName();
}
}
六、Spring Boot中的IoC最佳实践
6.1 自动配置原理
Spring Boot的自动配置基于条件化Bean注册和@EnableAutoConfiguration:
启动类@SpringBootApplication
@EnableAutoConfiguration
spring.factories中查找AutoConfiguration类
过滤掉不满足@Conditional的配置
注册符合条件的Bean
自定义自动配置示例:
// 1. 定义自动配置类
@Configuration
@ConditionalOnClass(MyService.class) // 当类路径下有MyService时生效
@EnableConfigurationProperties(MyServiceProperties.class) // 启用配置属性
@AutoConfigureAfter(DataSourceAutoConfiguration.class) // 在DataSource配置之后
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 当容器中没有MyService时创建
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getPrefix(), properties.getSuffix());
}
}
// 2. 定义配置属性类
@ConfigurationProperties("my.service")
public class MyServiceProperties {
private String prefix = "Default";
private String suffix = "!";
// getters and setters...
}
// 3. 在META-INF/spring.factories中注册
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyServiceAutoConfiguration
6.2 多环境配置管理
Spring Boot支持多种环境配置方式:
// 1. 主配置类
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig {
@Bean
@Profile("dev") // 只在dev环境激活
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:dev-schema.sql")
.build();
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
// 生产环境数据源配置
}
}
// 2. 使用@Conditional根据环境变量配置
@Bean
@ConditionalOnExpression("'${spring.profiles.active}' == 'cloud'")
public CloudService cloudService() {
return new CloudService();
}
// 3. 属性文件组织:
// application.properties - 公共配置
// application-dev.properties - 开发环境
// application-prod.properties - 生产环境
6.3 测试中的IoC应用
Spring Boot提供了强大的测试支持:
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@MockBean // 替换真实Bean为Mock
private PaymentService paymentService;
@Test
void testOrderWithMockPayment() {
// 设置Mock行为
when(paymentService.process(any())).thenReturn(true);
Order order = new Order("test");
boolean result = orderService.placeOrder(order);
assertTrue(result);
verify(paymentService).process(any());
}
@Test
@ActiveProfiles("test") // 使用test环境配置
void testWithTestProfile() {
// 测试特定环境的配置
}
}
七、常见问题与解决方案
7.1 IoC常见问题排查表
问题现象 | 可能原因 | 解决方案 |
NoSuchBeanDefinitionException | 1. Bean未扫描到 2. 条件不满足 3. 名称错误 | 1. 检查@ComponentScan范围 2. 检查@Conditional条件 3. 检查Bean名称 |
BeanCurrentlyInCreationException | 循环依赖 | 1. 改为setter注入 2. 使用@Lazy延迟加载 |
UnsatisfiedDependencyException | 依赖注入失败 | 1. 检查依赖Bean是否存在 2. 检查@Primary/@Qualifier配置 |
BeanNotOfRequiredTypeException | 类型不匹配 | 1. 检查Bean实现类 2. 检查泛型类型 |
NoUniqueBeanDefinitionException | 多个同类型Bean | 1. 使用@Primary指定首选 2. 使用@Qualifier指定名称 |
7.2 性能优化建议
- 合理使用作用域:
- 无状态服务使用singleton
- 有状态对象使用prototype
- Web相关使用request/session
- 延迟初始化:
- # application.properties中全局设置
spring.main.lazy-initialization=true - 组件扫描优化:
- @SpringBootApplication(scanBasePackages = "com.myapp")
// 替代默认的全包扫描 - 配置类分离:
- @Configuration
@Profile("prod")
public class ProdConfig {
// 生产环境特有配置
} - BeanPostProcessor优化:
- 尽量缩小处理范围
- 实现Ordered接口控制执行顺序
结语
Spring IoC容器是Spring框架的核心,理解其工作原理和最佳实践对于构建高质量Spring应用至关重要。通过本文的系统学习,你应该已经掌握了从基础到高级的IoC知识,包括:
- IoC的基本概念和Spring实现
- Bean的生命周期和作用域管理
- 多种依赖注入方式的对比和实践
- 高级特性如条件化注册、自定义作用域
- Spring Boot中的自动配置原理
- 常见问题排查和性能优化
在实际开发中,建议:
- 优先使用构造器注入
- 合理设计Bean的作用域
- 善用条件化配置实现灵活装配
- 遵循"约定优于配置"原则
希望这篇全面深入的指南能帮助你在Spring Boot开发中更好地运用IoC技术,构建更加灵活、可维护的应用程序。
关注我?别别别,我怕你笑出腹肌找我赔钱。
头条对markdown的文章显示不太友好,想了解更多的可以关注微信公众号:“Eric的技术杂货库”,有更多的干货以及资料下载。
猜你喜欢
- 2025-07-23 Spring IoC Container 原理解析(spring中ioc的作用与原理)
- 2025-07-23 Spring之底层架构核心概念解析(spring底层设计模式)
- 2025-07-23 深入理解 JSR 303:数据校验在 Spring Boot 中的应用
- 2025-07-23 Springboot集成Kafka原理(kafka结合springboot)
- 2025-07-23 Spring如何加载「IOC容器」以及「装载Bean」源码解读
- 2025-07-23 Spring Security 自动踢掉前一个登录用户,一个配置搞定
- 2025-07-23 spring cloud Alibaba参考的中文文档
- 2025-07-23 Spring Boot执行过程(执行springboot的jar)
- 2025-07-23 SpringBoot中6种拦截器使用场景(springboot拦截器放行)
- 2025-07-23 Java开发200+个学习知识路线-史上最全(框架篇)
你 发表评论:
欢迎- 07-23Spring IoC Container 原理解析(spring中ioc的作用与原理)
- 07-23Spring之底层架构核心概念解析(spring底层设计模式)
- 07-23深入理解 JSR 303:数据校验在 Spring Boot 中的应用
- 07-23Springboot集成Kafka原理(kafka结合springboot)
- 07-23Spring如何加载「IOC容器」以及「装载Bean」源码解读
- 07-23Spring Security 自动踢掉前一个登录用户,一个配置搞定
- 07-23Spring Boot 控制反转(IoC)全面解析:从基础到高级实践
- 07-23spring cloud Alibaba参考的中文文档
- 最近发表
-
- Spring IoC Container 原理解析(spring中ioc的作用与原理)
- Spring之底层架构核心概念解析(spring底层设计模式)
- 深入理解 JSR 303:数据校验在 Spring Boot 中的应用
- Springboot集成Kafka原理(kafka结合springboot)
- Spring如何加载「IOC容器」以及「装载Bean」源码解读
- Spring Security 自动踢掉前一个登录用户,一个配置搞定
- Spring Boot 控制反转(IoC)全面解析:从基础到高级实践
- spring cloud Alibaba参考的中文文档
- Spring Boot执行过程(执行springboot的jar)
- SpringBoot中6种拦截器使用场景(springboot拦截器放行)
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- flutterrun (59)
- 系统设计图 (58)
- powershellfor (73)
- messagesource (71)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- qcombobox样式表 (68)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)