2-1 IoC 容器与依赖注入

0 阅读5分钟

2-1 IoC 容器与依赖注入

概念解析

什么是 IoC?

IoC(Inversion of Control)控制反转:将对象的创建和管理权交给容器,而不是在代码中 new

DI(Dependency Injection)依赖注入:容器在运行时自动将依赖注入到对象中

Bean 注册方式

方式注解适用场景
组件扫描@Component, @Service, @Repository, @Controller大部分业务 Bean
@Bean 注解@Configuration + @Bean第三方库、需要精细控制
@Import@Import(ConfigClass.class)批量导入配置
FactoryBean实现 FactoryBean 接口复杂创建逻辑
动态注册BeanDefinitionRegistry动态注册Bean

Bean 作用域

作用域说明使用场景
singleton单例(默认)无状态 Bean
prototype原型,每次新建有状态的 Bean
request一次请求Web 请求相关
session一次会话用户会话信息
application应用级别全局共享
websocketWebSocket 生命周期长连接

代码示例

1. 基本注入方式

构造器注入(推荐)

@Service
public class UserService {

    private final UserRepository userRepository;

    // 构造器注入,final 不可变
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Setter 注入

@Service
public class UserService {

    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

字段注入

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;  // 不推荐
}

2. @Bean 方式注册

@Configuration
public class AppConfig {

    @Bean
    // 默认方法名就是 bean 名称
    public UserService userService() {
        return new UserServiceImpl();
    }

    @Bean(name = "customUserService")  // 指定名称
    public UserService userService2() {
        return new UserServiceImpl();
    }
}

3. @Configuration 深度解析

@Configuration
public class AppConfig {

    @Bean
    public UserService userService(UserRepository userRepository) {
        // 参数会自动注入
        return new UserServiceImpl(userRepository);
    }

    // @Scope 调整作用域
    @Bean
    @Scope("prototype")
    public UserService prototypeService() {
        return new UserServiceImpl();
    }

    // @Lazy 延迟初始化
    @Bean
    @Lazy
    public UserService lazyService() {
        return new UserServiceImpl();
    }

    // @Primary 优先使用
    @Bean
    @Primary
    public UserRepository primaryRepository() {
        return new PrimaryUserRepository();
    }
}

4. Bean 条件注册

@Configuration
public class ConditionalConfig {

    // 类存在时才注册
    @Bean
    @ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
    public JsonService jsonService() {
        return new JacksonJsonService();
    }

    // 配置项存在时才注册
    @Bean
    @ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
    public CacheService cacheService() {
        return new RedisCacheService();
    }

    // Bean 不存在时才注册
    @Bean
    @ConditionalOnMissingBean(CacheService.class)
    public CacheService defaultCacheService() {
        return new SimpleCacheService();
    }

    // 容器中有特定类型时才注册
    @Bean
    @ConditionalOnBean(DataSource.class)
    public JdbcService jdbcService() {
        return new JdbcServiceImpl();
    }
}

5. Bean 生命周期回调

@Configuration
public class LifecycleConfig {

    @Bean(initMethod = "init", destroyMethod = "cleanup")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    // 或使用注解
    @Component
    public class MyService {

        @PostConstruct
        public void init() {
            // 初始化操作
        }

        @PreDestroy
        public void destroy() {
            // 销毁操作
        }
    }
}

源码解读

Bean 创建流程

// AbstractApplicationContext.refresh() 核心流程
public void refresh() throws BeansException {
    // 1. 准备 BeanFactory
    prepareBeanFactory(beanFactory);

    // 2. 执行 BeanFactoryPostProcessor
    invokeBeanFactoryPostProcessors(beanFactory);

    // 3. 注册 BeanPostProcessor
    registerBeanPostProcessors(beanFactory);

    // 4. 初始化消息源、事件广播器等

    // 5. 创建 Bean(实例化 + 依赖注入)
    finishBeanFactoryInitialization(beanFactory);

    // 6. 初始化所有 Bean
    finishRefresh();
}

Bean 实例化时机

类型实例化时机
singleton容器创建时(默认)
prototype每次 getBean 时
lazy singleton第一次使用时

常见坑点

⚠️ 坑 1:循环依赖

问题:A 依赖 B,B 依赖 A

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;  // 循环依赖!
}

解决

// 方案一:使用 @Lazy 延迟加载
@Autowired
private B b;

// 方案二:使用 Setter 注入(用于 break 循环)
@Component
public class A {
    private B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

// 方案三:使用 @PostConstruct
@Component
public class A {
    @Autowired
    private B b;

    @PostConstruct
    public void init() {
        b.setA(this);
    }
}

注意:Spring Boot 2.6+ 默认禁止循环依赖,需要配置开启

⚠️ 坑 2:多个同类型 Bean 注入

问题:有多个实现类,@Autowired 时不知道注入哪个

// 有两个实现类
@Component
public class AlipayService implements PayService { }

@Component
public class WechatPayService implements PayService { }

// 注入时报错
@Autowired
private PayService payService;  // NoUniqueBeanDefinitionException

解决

// 方案一:@Primary 指定默认实现
@Component
@Primary
public class AlipayService implements PayService { }

// 方案二:@Qualifier 指定名称
@Autowired
@Qualifier("alipayService")
private PayService payService;

// 方案三:注入 List
@Autowired
private List<PayService> payServices;  // 注入所有实现

⚠️ 坑 3:静态字段注入

问题:static 字段无法通过 @Autowired 注入

错误写法

@Component
public class UserService {
    @Autowired
    private static UserRepository userRepository;  // 错误!
}

正确写法

@Component
public class UserService {
    private static UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        UserService.userRepository = userRepository;
    }
}

面试题

Q1:Spring IoC 的实现原理?

参考答案

  1. 配置文件解析:读取 XML 或注解配置
  2. BeanDefinition:将 Bean 定义转为 BeanDefinition 对象
  3. BeanFactoryPostProcessor:允许在 Bean 创建前修改 BeanDefinition
  4. 实例化:通过反射(Constructor.newInstance)创建实例
  5. 依赖注入:通过反射设置属性值
  6. 初始化:执行 BeanPostProcessor 后置处理
  7. 生命周期管理:注册 DisposableBean 销毁回调

Q2:构造器注入为什么是推荐的?

参考答案

  1. 不可变性:构造器注入的对象是 final,无法被修改
  2. 强制依赖:不提供构造器参数会导致编译失败
  3. 易于测试:可以直接 new 传入 mock 对象
  4. 线程安全:不存在中途被修改的风险
  5. 避免 NPE:构造器参数必须非空

Q3:BeanFactory 和 FactoryBean 的区别?

参考答案

维度BeanFactoryFactoryBean
定位IoC 容器接口创建 Bean 的工厂
作用管理 Bean 生命周期封装复杂创建逻辑
获取方式getBean("beanName")getBean("&beanName")
使用场景框架内部开发者自定义
// FactoryBean 示例
@Component
public class MyFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() {
        // 复杂创建逻辑
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

// 获取 MyFactoryBean 本身:ctx.getBean("&myFactoryBean")
// 获取 MyFactoryBean 创建的对象:ctx.getBean("myFactoryBean")

Q4:Bean 的生命周期?

参考答案

1. 实例化 Bean
      ↓
2. 设置属性值(依赖注入)
      ↓
3. 检查 Aware 接口
      ↓
4. BeanPostProcessor.postProcessBeforeInitialization
      ↓
5. @PostConstruct 初始化方法
      ↓
6. InitializingBean.afterPropertiesSet
      ↓
7. 自定义 init-method8. BeanPostProcessor.postProcessAfterInitialization
      ↓
9. Bean 已就绪
      ↓
10. @PreDestroy 销毁方法
      ↓
11. DisposableBean.destroy
      ↓
12. 自定义 destroy-method

Q5:@Autowired 和 @Resource 的区别?

参考答案

特性@Autowired@Resource
来源Spring 注解Java JSR-250 注解
注入方式byType + byNamebyName 优先
required 属性支持不支持
位置字段/构造器/Setter字段/Setter
// @Autowired 默认按类型匹配
@Autowired
private UserService userService;

// 可以配合 @Qualifier 指定名称
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;

// @Resource 默认按名称匹配,匹配失败再按类型
@Resource(name = "userServiceImpl")
private UserService userService;