IoC 容器
深入了解 LCGYL Framework 的 IoC(控制反转)容器,掌握依赖注入的高级用法。
版本说明
本文档基于 LCGYL Framework 2.1 版本,包含最新的 @Primary、@Lazy、@PostConstruct、@Conditional 等注解支持。
容器概述
IoC 容器是 LCGYL Framework 的核心,负责管理组件的生命周期和依赖关系。
核心概念
- 控制反转(IoC):将对象创建和依赖管理的控制权交给容器
- 依赖注入(DI):容器自动注入对象所需的依赖
- 组件(Component):由容器管理的对象
- 作用域(Scope):组件的生命周期范围
核心注解一览
| 注解 | 说明 | 版本 |
|---|---|---|
@Component | 标记组件 | 2.0+ |
@Inject | 依赖注入 | 2.0+ |
@Primary | 主候选 Bean | 2.1+ |
@Lazy | 延迟初始化 | 2.1+ |
@PostConstruct | 初始化方法 | 2.1+ |
@Conditional | 条件注册 | 2.1+ |
容器初始化
自动初始化
java
public class Application {
public static void main(String[] args) {
// 容器自动初始化
LcgylApplication app = LcgylApplication.run(Application.class, args);
}
}手动初始化
java
// 创建容器
Container container = new Container();
// 注册组件
container.register(UserService.class);
container.register(UserRepository.class);
// 初始化容器
container.initialize();
// 获取组件
UserService service = container.getBean(UserService.class);组件注册
注解注册
使用 @Component 注解自动注册。
java
@Component
public class UserService {
// 自动注册到容器
}编程式注册
java
container.register(UserService.class);
container.register(UserRepository.class);
// 注册为单例
container.registerSingleton(CacheService.class);
// 注册为原型
container.registerPrototype(TaskProcessor.class);工厂方法注册
java
@Component
public class ServiceFactory {
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
return new HikariDataSource(config);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}依赖解析
构造器注入
容器自动解析构造器参数。
java
@Component
public class UserService {
private final UserRepository repository;
private final EventBus eventBus;
// 容器自动注入依赖
@Inject
public UserService(UserRepository repository, EventBus eventBus) {
this.repository = repository;
this.eventBus = eventBus;
}
}循环依赖检测
容器会检测并报告循环依赖。
java
// ❌ 循环依赖
@Component
public class ServiceA {
@Inject
private ServiceB serviceB;
}
@Component
public class ServiceB {
@Inject
private ServiceA serviceA;
}
// 启动时抛出 CircularDependencyException依赖树
java
// 查看依赖树
container.printDependencyTree(UserService.class);
// 输出:
// UserService
// ├── UserRepository
// │ └── JdbcTemplate
// │ └── DataSource
// └── EventBus作用域管理
单例作用域
默认作用域,整个应用只创建一个实例。
java
@Component // 默认单例
public class UserService {
}
// 多次获取返回同一实例
UserService service1 = container.getBean(UserService.class);
UserService service2 = container.getBean(UserService.class);
assert service1 == service2; // true原型作用域
每次获取都创建新实例。
java
@Component(scope = Scope.PROTOTYPE)
public class TaskProcessor {
}
// 每次获取返回新实例
TaskProcessor processor1 = container.getBean(TaskProcessor.class);
TaskProcessor processor2 = container.getBean(TaskProcessor.class);
assert processor1 != processor2; // true请求作用域
每个请求创建一个实例(Web 应用)。
java
@Component(scope = Scope.REQUEST)
public class RequestContext {
private String requestId;
private User currentUser;
}会话作用域
每个会话创建一个实例(Web 应用)。
java
@Component(scope = Scope.SESSION)
public class ShoppingCart {
private List<Item> items = new ArrayList<>();
}组件查找
按类型查找
java
// 获取单个组件
UserService service = container.getBean(UserService.class);
// 获取所有实现
List<MessageHandler> handlers = container.getBeans(MessageHandler.class);按名称查找
java
@Component("userService")
public class UserService {
}
// 按名称获取
UserService service = container.getBean("userService", UserService.class);按限定符查找
java
@Component
@Qualifier("mysql")
public class MySQLUserRepository implements UserRepository {
}
@Component
@Qualifier("mongodb")
public class MongoDBUserRepository implements UserRepository {
}
// 按限定符获取
UserRepository repo = container.getBean(UserRepository.class, "mysql");使用 @Primary 注解 2.1+
当存在多个同类型的 Bean 时,使用 @Primary 标记首选的 Bean。
java
public interface PaymentService {
void pay(BigDecimal amount);
}
@Component
@Primary // 标记为首选
public class AlipayService implements PaymentService {
@Override
public void pay(BigDecimal amount) {
// 支付宝支付
}
}
@Component
public class WechatPayService implements PaymentService {
@Override
public void pay(BigDecimal amount) {
// 微信支付
}
}
// 注入时自动选择 @Primary 标记的 Bean
@Component
public class OrderService {
private final PaymentService paymentService;
@Inject
public OrderService(PaymentService paymentService) {
// 自动注入 AlipayService
this.paymentService = paymentService;
}
}使用场景
- 提供默认实现
- 多实现中指定首选
- 避免
NoUniqueBeanDefinitionException
条件查找
java
// 检查组件是否存在
if (container.containsBean(CacheService.class)) {
CacheService cache = container.getBean(CacheService.class);
}
// 获取可选组件
Optional<CacheService> cache = container.getBeanOptional(CacheService.class);组件扫描
自动扫描
java
@ComponentScan("com.example.myapp")
public class Application {
public static void main(String[] args) {
Application.run(Application.class, args);
}
}指定多个包
java
@ComponentScan({
"com.example.myapp.service",
"com.example.myapp.repository",
"com.example.myapp.controller"
})
public class Application {
}排除组件
java
@ComponentScan(
basePackages = "com.example.myapp",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Deprecated.class
)
)
public class Application {
}包含组件
java
@ComponentScan(
basePackages = "com.example.myapp",
includeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = Service.class
)
)
public class Application {
}条件注册 2.1+
条件注册允许根据运行时条件决定是否注册 Bean,实现灵活的配置。
@Conditional 基础用法
java
// 自定义条件
public class OnLinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context) {
String os = System.getProperty("os.name").toLowerCase();
return os.contains("linux");
}
}
@Component
@Conditional(OnLinuxCondition.class)
public class LinuxSpecificService {
// 只在 Linux 系统上注册
}基于配置属性
java
@Component
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public class RedisCacheService implements CacheService {
// 只有当 cache.enabled=true 时才注册
}
// 配置文件
// cache.enabled=true基于类存在
java
@Component
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
public class JedisCacheService implements CacheService {
// 只有当 Jedis 类存在时才注册
}
@Component
@ConditionalOnClass(name = "io.lettuce.core.RedisClient")
public class LettuceCacheService implements CacheService {
// 只有当 Lettuce 类存在时才注册
}基于 Bean 存在
java
@Component
@ConditionalOnBean(DataSource.class)
public class JdbcTemplate {
// 只有当 DataSource Bean 存在时才注册
}基于 Bean 缺失
java
@Component
@ConditionalOnMissingBean(CacheService.class)
public class DefaultCacheService implements CacheService {
// 只有当没有其他 CacheService 实现时才注册
// 提供默认实现
}组合条件
java
@Component
@ConditionalOnProperty(name = "redis.enabled", havingValue = "true")
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
public class RedisService {
// 同时满足:配置启用 AND Jedis 类存在
}条件注册最佳实践
java
// ✅ 推荐:提供默认实现
@Component
@Primary
@ConditionalOnMissingBean(CacheService.class)
public class InMemoryCacheService implements CacheService {
// 默认的内存缓存实现
}
// ✅ 推荐:按环境配置
@Component
@ConditionalOnProperty(name = "app.env", havingValue = "production")
public class ProductionEmailService implements EmailService {
// 生产环境的邮件服务
}
@Component
@ConditionalOnProperty(name = "app.env", havingValue = "development")
public class MockEmailService implements EmailService {
// 开发环境的模拟邮件服务
}条件注册的优势
- 灵活配置:根据环境自动选择实现
- 模块化:按需加载功能模块
- 测试友好:轻松切换测试实现
- 零配置:提供合理的默认值
容器事件
监听容器事件
java
@Component
public class ContainerEventListener {
@Subscribe
public void onContainerStarted(ContainerStartedEvent event) {
System.out.println("容器已启动");
}
@Subscribe
public void onBeanCreated(BeanCreatedEvent event) {
System.out.println("组件已创建: " + event.getBeanClass().getName());
}
@Subscribe
public void onContainerStopped(ContainerStoppedEvent event) {
System.out.println("容器已停止");
}
}容器扩展
BeanPostProcessor
在组件初始化前后执行逻辑。
java
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化前: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("初始化后: " + beanName);
return bean;
}
}BeanFactoryPostProcessor
在容器初始化前修改组件定义。
java
@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 修改组件定义
BeanDefinition definition = beanFactory.getBeanDefinition("userService");
definition.setScope(Scope.PROTOTYPE);
}
}生命周期管理 2.1+
@PostConstruct 初始化方法
使用 @PostConstruct 注解标记初始化方法,在依赖注入完成后自动调用。
java
@Component
public class DatabaseService {
private final DataSource dataSource;
private Connection connection;
@Inject
public DatabaseService(DataSource dataSource) {
this.dataSource = dataSource;
}
@PostConstruct
public void init() {
// 依赖注入完成后执行
this.connection = dataSource.getConnection();
System.out.println("数据库连接已建立");
}
}注意事项
@PostConstruct方法必须是public void- 不能有参数
- 一个类只能有一个
@PostConstruct方法 - 在构造器注入之后、Bean 可用之前调用
BeanPostProcessor 后处理器
实现 BeanPostProcessor 接口,在 Bean 初始化前后执行自定义逻辑。
java
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化前: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("初始化后: " + beanName);
// 可以返回代理对象
return bean;
}
}生命周期顺序
1. 实例化 Bean(调用构造器)
2. 依赖注入(@Inject)
3. BeanPostProcessor.postProcessBeforeInitialization()
4. @PostConstruct 方法
5. BeanPostProcessor.postProcessAfterInitialization()
6. Bean 可用性能优化
@Lazy 延迟初始化 2.1+
使用 @Lazy 注解延迟 Bean 的初始化,直到首次使用时才创建。
java
@Component
@Lazy
public class HeavyService {
public HeavyService() {
// 耗时的初始化操作
System.out.println("HeavyService 初始化中...");
loadLargeData();
}
private void loadLargeData() {
// 加载大量数据
}
}
// 使用示例
@Component
public class AppService {
private final HeavyService heavyService;
@Inject
public AppService(@Lazy HeavyService heavyService) {
// 此时 HeavyService 还未初始化
this.heavyService = heavyService;
}
public void doWork() {
// 首次调用时才初始化 HeavyService
heavyService.process();
}
}使用场景
- 启动时间优化
- 按需加载重量级服务
- 解决循环依赖(谨慎使用)
并行初始化
java
Application app = Application.builder()
.parallelInitialization(true)
.run(args);缓存组件
java
// 容器自动缓存单例组件
UserService service1 = container.getBean(UserService.class);
UserService service2 = container.getBean(UserService.class);
// 不会重复创建,直接返回缓存的实例容器层次结构
父子容器
java
// 创建父容器
Container parent = new Container();
parent.register(SharedService.class);
parent.initialize();
// 创建子容器
Container child = new Container(parent);
child.register(ChildService.class);
child.initialize();
// 子容器可以访问父容器的组件
SharedService shared = child.getBean(SharedService.class);
// 父容器不能访问子容器的组件
// ChildService service = parent.getBean(ChildService.class); // 抛出异常最佳实践
1. 优先使用构造器注入
java
// ✅ 推荐
@Component
public class UserService {
private final UserRepository repository;
@Inject
public UserService(UserRepository repository) {
this.repository = repository;
}
}2. 避免循环依赖
java
// ✅ 推荐:重构代码
@Component
public class CommonService {
// 提取公共逻辑
}
@Component
public class ServiceA {
@Inject
private CommonService commonService;
}
@Component
public class ServiceB {
@Inject
private CommonService commonService;
}3. 合理使用作用域
java
// ✅ 无状态服务使用单例
@Component
public class UserService {
}
// ✅ 有状态对象使用原型
@Component(scope = Scope.PROTOTYPE)
public class TaskProcessor {
private Task currentTask;
}4. 使用接口编程
java
// ✅ 推荐:依赖接口
@Component
public class UserService {
private final UserRepository repository; // 接口
@Inject
public UserService(UserRepository repository) {
this.repository = repository;
}
}5. 组件职责单一
java
// ✅ 推荐:职责单一
@Component
public class UserService {
// 只负责用户业务逻辑
}
@Component
public class UserRepository {
// 只负责数据访问
}常见问题
Q: 容器何时初始化组件?
A:
- 单例组件:容器启动时初始化
- 原型组件:每次获取时创建
- 延迟组件:首次使用时初始化
Q: 如何调试容器问题?
A: 启用调试日志。
properties
# application.properties
logging.level.com.lcgyl.framework.core.container=DEBUGyaml
# application.yaml
logging:
level:
com.lcgyl.framework.core.container: DEBUGQ: 容器性能如何?
A:
- 启动时间:毫秒级(取决于组件数量)
- 内存占用:极小(只存储组件引用)
- 获取性能:纳秒级(直接从缓存获取)