Skip to content

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主候选 Bean2.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=DEBUG
yaml
# application.yaml
logging:
  level:
    com.lcgyl.framework.core.container: DEBUG

Q: 容器性能如何?

A:

  • 启动时间:毫秒级(取决于组件数量)
  • 内存占用:极小(只存储组件引用)
  • 获取性能:纳秒级(直接从缓存获取)

下一步

Released under the Apache License 2.0