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

SpringBoot3.x入门到精通系列:2.2 依赖注入与IoC容器

SpringBoot 3.x 依赖注入与IoC容器

🎯 IoC容器概述

IoC (Inversion of Control) 控制反转是Spring框架的核心特性。它将对象的创建、配置和管理交给Spring容器,而不是由对象自己控制。

核心概念

  • IoC容器: 负责管理对象的生命周期
  • Bean: 由Spring容器管理的对象
  • 依赖注入: 容器将依赖关系注入到对象中
  • 控制反转: 对象不再主动创建依赖,而是被动接收

🔧 依赖注入方式

1. 构造函数注入 (推荐)

@Service
public class UserService {private final UserRepository userRepository;private final EmailService emailService;// 构造函数注入 - 推荐方式public UserService(UserRepository userRepository, EmailService emailService) {this.userRepository = userRepository;this.emailService = emailService;}public User createUser(User user) {User savedUser = userRepository.save(user);emailService.sendWelcomeEmail(savedUser.getEmail());return savedUser;}
}

优点:

  • 保证依赖不可变 (final字段)
  • 保证依赖不为null
  • 便于单元测试
  • 避免循环依赖

2. Setter注入

@Service
public class OrderService {private PaymentService paymentService;private InventoryService inventoryService;@Autowiredpublic void setPaymentService(PaymentService paymentService) {this.paymentService = paymentService;}@Autowiredpublic void setInventoryService(InventoryService inventoryService) {this.inventoryService = inventoryService;}public Order processOrder(Order order) {inventoryService.reserveItems(order.getItems());paymentService.processPayment(order.getPayment());return order;}
}

3. 字段注入 (不推荐)

@Service
public class ProductService {@Autowiredprivate ProductRepository productRepository;@Autowiredprivate CategoryService categoryService;public Product createProduct(Product product) {// 业务逻辑return productRepository.save(product);}
}

缺点:

  • 难以进行单元测试
  • 违反了封装原则
  • 可能导致空指针异常

📋 Bean的定义与管理

1. 使用注解定义Bean

// 组件注解
@Component
public class CommonComponent {public void doSomething() {System.out.println("Common component working...");}
}// 服务层注解
@Service
public class BusinessService {public String processData(String data) {return "Processed: " + data;}
}// 数据访问层注解
@Repository
public class DataRepository {public void saveData(String data) {System.out.println("Saving data: " + data);}
}// 控制器注解
@Controller
public class WebController {@Autowiredprivate BusinessService businessService;@RequestMapping("/process")@ResponseBodypublic String process(@RequestParam String data) {return businessService.processData(data);}
}

2. 使用@Configuration和@Bean

@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/demo");dataSource.setUsername("root");dataSource.setPassword("password");dataSource.setMaximumPoolSize(20);return dataSource;}@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}@Bean@Primary  // 当有多个相同类型的Bean时,优先使用此Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());return template;}@Bean@Qualifier("stringRedisTemplate")  // 指定Bean的名称public RedisTemplate<String, String> stringRedisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, String> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new StringRedisSerializer());return template;}
}

3. Bean的作用域

@Component
@Scope("singleton")  // 单例模式 (默认)
public class SingletonBean {private int counter = 0;public int increment() {return ++counter;}
}@Component
@Scope("prototype")  // 原型模式,每次获取都创建新实例
public class PrototypeBean {private final String id = UUID.randomUUID().toString();public String getId() {return id;}
}@Component
@Scope("request")  // 请求作用域,每个HTTP请求创建一个实例
public class RequestScopedBean {private String requestId;@PostConstructpublic void init() {requestId = "REQ-" + System.currentTimeMillis();}public String getRequestId() {return requestId;}
}@Component
@Scope("session")  // 会话作用域,每个HTTP会话创建一个实例
public class SessionScopedBean {private String sessionId;@PostConstructpublic void init() {sessionId = "SES-" + System.currentTimeMillis();}public String getSessionId() {return sessionId;}
}

🔄 Bean的生命周期

1. 生命周期回调

@Component
public class LifecycleBean implements InitializingBean, DisposableBean {private String name;public LifecycleBean() {System.out.println("1. 构造函数调用");}@PostConstructpublic void postConstruct() {System.out.println("2. @PostConstruct 方法调用");this.name = "Initialized Bean";}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("3. InitializingBean.afterPropertiesSet() 调用");}@PreDestroypublic void preDestroy() {System.out.println("4. @PreDestroy 方法调用");}@Overridepublic void destroy() throws Exception {System.out.println("5. DisposableBean.destroy() 调用");}public String getName() {return name;}
}

2. 使用@Bean的初始化和销毁方法

@Configuration
public class BeanConfig {@Bean(initMethod = "init", destroyMethod = "cleanup")public CustomService customService() {return new CustomService();}
}public class CustomService {public void init() {System.out.println("CustomService 初始化");}public void cleanup() {System.out.println("CustomService 清理资源");}public void doWork() {System.out.println("CustomService 工作中...");}
}

🎯 高级依赖注入特性

1. 条件化Bean创建

@Configuration
public class ConditionalConfig {@Bean@ConditionalOnProperty(name = "feature.email.enabled", havingValue = "true")public EmailService emailService() {return new SmtpEmailService();}@Bean@ConditionalOnMissingBean(EmailService.class)public EmailService mockEmailService() {return new MockEmailService();}@Bean@ConditionalOnClass(RedisTemplate.class)public CacheService redisCacheService(RedisTemplate<String, Object> redisTemplate) {return new RedisCacheService(redisTemplate);}@Bean@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")public CacheService memoryCacheService() {return new MemoryCacheService();}
}

2. 集合注入

// 定义多个相同接口的实现
@Component
public class EmailNotificationHandler implements NotificationHandler {@Overridepublic void handle(String message) {System.out.println("Email: " + message);}
}@Component
public class SmsNotificationHandler implements NotificationHandler {@Overridepublic void handle(String message) {System.out.println("SMS: " + message);}
}@Component
public class PushNotificationHandler implements NotificationHandler {@Overridepublic void handle(String message) {System.out.println("Push: " + message);}
}// 注入所有实现
@Service
public class NotificationService {private final List<NotificationHandler> handlers;public NotificationService(List<NotificationHandler> handlers) {this.handlers = handlers;}public void sendNotification(String message) {handlers.forEach(handler -> handler.handle(message));}
}

3. 使用@Qualifier精确注入

@Configuration
public class DatabaseConfig {@Bean@Qualifier("primaryDataSource")public DataSource primaryDataSource() {// 主数据源配置return new HikariDataSource();}@Bean@Qualifier("secondaryDataSource")public DataSource secondaryDataSource() {// 从数据源配置return new HikariDataSource();}
}@Service
public class UserService {private final DataSource primaryDataSource;private final DataSource secondaryDataSource;public UserService(@Qualifier("primaryDataSource") DataSource primaryDataSource,@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {this.primaryDataSource = primaryDataSource;this.secondaryDataSource = secondaryDataSource;}public void createUser(User user) {// 使用主数据源}public List<User> findUsers() {// 使用从数据源return Collections.emptyList();}
}

4. 懒加载

@Component
@Lazy  // 懒加载,只有在第一次使用时才创建
public class HeavyService {public HeavyService() {System.out.println("HeavyService 创建 - 这是一个耗时的操作");// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}public void doHeavyWork() {System.out.println("执行重量级工作...");}
}@Service
public class BusinessService {private final HeavyService heavyService;// 即使注入了懒加载的Bean,也只有在实际使用时才会创建public BusinessService(@Lazy HeavyService heavyService) {this.heavyService = heavyService;System.out.println("BusinessService 创建完成");}public void performWork() {// 只有在这里才会真正创建HeavyServiceheavyService.doHeavyWork();}
}

🧪 单元测试中的依赖注入

1. 使用@MockBean

@SpringBootTest
class UserServiceTest {@Autowiredprivate UserService userService;@MockBeanprivate UserRepository userRepository;@MockBeanprivate EmailService emailService;@Testvoid testCreateUser() {// GivenUser user = new User("John", "john@example.com");User savedUser = new User(1L, "John", "john@example.com");when(userRepository.save(any(User.class))).thenReturn(savedUser);// WhenUser result = userService.createUser(user);// ThenassertThat(result.getId()).isEqualTo(1L);verify(emailService).sendWelcomeEmail("john@example.com");}
}

2. 使用@TestConfiguration

@TestConfiguration
public class TestConfig {@Bean@Primarypublic EmailService mockEmailService() {return Mockito.mock(EmailService.class);}
}@SpringBootTest
@Import(TestConfig.class)
class IntegrationTest {@Autowiredprivate UserService userService;@Autowiredprivate EmailService emailService;  // 这里会注入Mock对象@Testvoid testUserCreation() {// 测试逻辑}
}

📊 最佳实践

1. 依赖注入最佳实践

  • 优先使用构造函数注入
  • 避免循环依赖
  • 合理使用@Qualifier
  • 谨慎使用@Lazy

2. Bean定义最佳实践

@Configuration
public class OptimalConfig {// 使用 proxyBeanMethods = false 提高性能@Configuration(proxyBeanMethods = false)static class DatabaseConfiguration {@Beanpublic DataSource dataSource(DatabaseProperties properties) {return DataSourceBuilder.create().url(properties.getUrl()).username(properties.getUsername()).password(properties.getPassword()).build();}}// 使用条件注解避免不必要的Bean创建@Bean@ConditionalOnMissingBeanpublic CacheManager cacheManager() {return new ConcurrentMapCacheManager();}
}

🔗 下一篇

在下一篇文章中,我们将学习SpringBoot的Web开发基础,包括MVC架构、请求处理、响应格式等内容。


本文关键词: 依赖注入, IoC容器, Bean管理, 构造函数注入, 生命周期, 作用域

http://www.dtcms.com/a/311781.html

相关文章:

  • Spring AI MCP 服务端
  • 边缘智能网关在水务行业中的应用—龙兴物联
  • 沿街晾晒识别准确率↑32%:陌讯多模态融合算法实战解析
  • P2415 集合求和
  • Docker 镜像打包为 ZIP 文件便于分享和转发
  • linux ext4缩容home,扩容根目录
  • 【Kubernetes】Secret配置管理,安全管理敏感配置
  • Effective C++ 条款17:以独立语句将newed对象置入智能指针
  • Python 程序设计讲义(50):Python 的可迭代对象与迭代器
  • Flutter基础知识
  • SpringBoot与TurboGears2跨栈、整合AI服务、智能客服路由系统整合实战
  • SpringCloud学习第一季-4
  • 第15届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2024年3月10日真题
  • 17、原坐标变换和逆变换在实战中用法
  • 无人机数字图传技术的前沿探索与应用
  • 【昇腾推理PaddleOCR】生产级部署方式
  • 机器学习实战:KNN算法全解析 - 从原理到创新应用
  • LangChain框架入门05:输出解析器使用技巧
  • SpringBoot 服务器配置
  • Json简单的实现
  • 【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
  • 【Leetcode】2561. 重排水果
  • 【Django】-6- 登录用户身份鉴权
  • 知识随记-----Qt 实战教程:使用 QNetworkAccessManager 发送 HTTP POST
  • 面试小总结
  • 解决技术问题思路
  • STM32学习记录--Day6
  • Spring 中 Bean 的生命周期
  • 知识蒸馏 - 基于KL散度的知识蒸馏 HelloWorld 示例
  • Linux网络编程【UDP网络通信demon】