Spring IOC(控制反转)详解

一、什么是 IOC

IOC(Inversion of Control,控制反转) 是一种设计思想,不是什么技术实现。它指的是将对象的创建、管理和依赖关系的控制权从程序代码本身转移到外部容器(Spring 容器)。

传统方式 vs IOC

传统方式(程序控制):

public class UserService {
    private UserDao userDao = new UserDao();  // 自己创建依赖对象
    
    public void addUser() {
        userDao.save();
    }
}

IOC 方式(容器控制):

public class UserService {
    @Autowired
    private UserDao userDao;  // 容器注入依赖对象
    
    public void addUser() {
        userDao.save();
    }
}

二、IOC 的核心概念

1. 控制反转

  • 控制:指对象创建、依赖管理的控制权
  • 反转:从程序代码反转到外部容器

2. 依赖注入(DI,Dependency Injection)

DI 是 IOC 的实现方式,指容器在创建对象时,自动将依赖对象注入到目标对象中。

3. IOC 容器

Spring 提供的容器实现,负责管理 Bean 的生命周期和依赖关系。

三、Spring IOC 容器

1. 两个核心接口

// BeanFactory:基础容器,提供基本的 IOC 功能
public interface BeanFactory {
    Object getBean(String name);
    <T> T getBean(Class<T> requiredType);
    boolean containsBean(String name);
    // ...
}

// ApplicationContext:高级容器,扩展了更多功能
public interface ApplicationContext extends BeanFactory {
    // 国际化支持
    String getMessage(String code, Object[] args, Locale locale);
    // 资源加载
    Resource getResource(String location);
    // 事件发布
    void publishEvent(ApplicationEvent event);
    // ...
}

2. 常见的实现类

// 1. ClassPathXmlApplicationContext:从类路径加载配置
ApplicationContext context = 
    new ClassPathXmlApplicationContext("applicationContext.xml");

// 2. FileSystemXmlApplicationContext:从文件系统加载配置
ApplicationContext context = 
    new FileSystemXmlApplicationContext("D:/config/applicationContext.xml");

// 3. AnnotationConfigApplicationContext:基于注解的配置
ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

// 4. WebApplicationContext:Web 应用专用
// 在 Spring Boot 中自动创建

四、Bean 的定义方式

1. XML 配置方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- 定义 Bean -->
    <bean id="userService" class="com.example.UserService">
        <!-- 构造器注入 -->
        <constructor-arg ref="userDao"/>
        <!-- 属性注入 -->
        <property name="name" value="张三"/>
    </bean>
    
    <bean id="userDao" class="com.example.UserDao"/>
</beans>

2. 注解方式(推荐)

// 1. @Component:通用组件
@Component
public class UserService {
    @Autowired
    private UserDao userDao;
}

// 2. @Service:服务层
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}

// 3. @Repository:数据访问层
@Repository
public class UserDao {
    public void save() {
        // ...
    }
}

// 4. @Controller:控制层
@Controller
public class UserController {
    @Autowired
    private UserService userService;
}

// 5. @Configuration:配置类
@Configuration
@ComponentScan("com.example")
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService(userDao());
    }
    
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }
}

五、依赖注入的方式

1. 构造器注入(推荐)

@Service
public class UserService {
    private final UserDao userDao;
    
    // Spring 4.3+ 单个构造器可省略 @Autowired
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

优点:

  • 保证依赖不可变(final)
  • 保证对象初始化完成
  • 更容易测试

2. Setter 注入

@Service
public class UserService {
    private UserDao userDao;
    
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

优点:

  • 灵活,可以在运行时修改
  • 适合可选依赖

3. 字段注入

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}

缺点:

  • 不能使用 final
  • 容易导致空指针
  • 不利于测试

4. 方法注入

@Service
public class UserService {
    private UserDao userDao;
    
    @Autowired
    public void init(UserDao userDao) {
        this.userDao = userDao;
    }
}

六、Bean 的作用域

// 1. singleton(默认):单例,整个容器只有一个实例
@Scope("singleton")
public class UserService {}

// 2. prototype:原型,每次获取都创建新实例
@Scope("prototype")
public class UserService {}

// 3. request:Web 应用,每个请求一个实例
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserService {}

// 4. session:Web 应用,每个会话一个实例
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserService {}

// 5. application:Web 应用,ServletContext 生命周期
@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserService {}

七、Bean 的生命周期

@Component
public class UserService implements BeanNameAware, BeanFactoryAware, 
        ApplicationContextAware, InitializingBean, DisposableBean {
    
    // 1. 实例化(构造器)
    public UserService() {
        System.out.println("1. 实例化");
    }
    
    // 2. 设置属性
    @Autowired
    private UserDao userDao;
    
    // 3. BeanNameAware
    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware.setBeanName: " + name);
    }
    
    // 4. BeanFactoryAware
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("4. BeanFactoryAware.setBeanFactory");
    }
    
    // 5. ApplicationContextAware
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        System.out.println("5. ApplicationContextAware.setApplicationContext");
    }
    
    // 6. BeanPostProcessor - 前置处理
    @PostConstruct
    public void postConstruct() {
        System.out.println("6. @PostConstruct");
    }
    
    // 7. InitializingBean
    @Override
    public void afterPropertiesSet() {
        System.out.println("7. InitializingBean.afterPropertiesSet");
    }
    
    // 8. 自定义初始化方法
    @Bean(initMethod = "init")
    public void init() {
        System.out.println("8. 自定义初始化方法");
    }
    
    // Bean 可以使用了
    
    // 9. PreDestroy
    @PreDestroy
    public void preDestroy() {
        System.out.println("9. @PreDestroy");
    }
    
    // 10. DisposableBean
    @Override
    public void destroy() {
        System.out.println("10. DisposableBean.destroy");
    }
    
    // 11. 自定义销毁方法
    @Bean(destroyMethod = "cleanup")
    public void cleanup() {
        System.out.println("11. 自定义销毁方法");
    }
}

八、IOC 容器启动流程

// 1. 创建容器
ApplicationContext context = 
    new AnnotationConfigApplicationContext(AppConfig.class);

// 内部流程:
// 1.1 扫描配置类,解析 Bean 定义
// 1.2 注册 Bean 定义到 BeanDefinitionRegistry
// 1.3 实例化单例 Bean(非懒加载)
// 1.4 执行依赖注入
// 1.5 执行初始化回调
// 1.6 容器启动完成

九、自动装配(@Autowired)

1. 按类型装配

@Autowired
private UserDao userDao;  // 按类型查找

2. 按名称装配

@Autowired
@Qualifier("userDaoImpl")  // 指定 Bean 名称
private UserDao userDao;

3. 可选依赖

@Autowired(required = false)  // 不存在也不报错
private UserDao userDao;

4. 集合注入

@Autowired
private List<UserDao> userDaoList;  // 注入所有 UserDao 类型

@Autowired
private Map<String, UserDao> userDaoMap;  // Bean名称 -> Bean实例

十、条件化注册 Bean

// 1. @Conditional
@Configuration
public class AppConfig {
    
    @Bean
    @Conditional(WindowsCondition.class)  // 满足条件才注册
    public UserService windowsService() {
        return new WindowsService();
    }
    
    @Bean
    @Conditional(LinuxCondition.class)
    public UserService linuxService() {
        return new LinuxService();
    }
}

// 2. 常用条件注解
@ConditionalOnClass(DataSource.class)  // 类路径存在
@ConditionalOnMissingBean(DataSource.class)  // 容器中不存在
@ConditionalOnProperty(name = "app.enabled", havingValue = "true")  // 配置属性
@ConditionalOnExpression("${app.enabled:true}")  // SpEL 表达式

十一、IOC 的优势

1. 解耦

// 不再需要手动创建和管理对象
// 对象之间的关系由容器维护

2. 便于测试

// 可以轻松注入 Mock 对象
@Test
public void testUserService() {
    UserDao mockDao = mock(UserDao.class);
    UserService service = new UserService(mockDao);
    // 测试...
}

3. 配置化管理

// 通过配置文件或注解管理对象
// 便于维护和修改

4. AOP 支持

// IOC 容器管理对象,便于实现 AOP
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.*.*(..))")
    public void logBefore() {
        // ...
    }
}

十二、总结

Spring IOC 的核心价值:

  1. 控制反转:将对象创建和依赖管理的控制权交给容器
  2. 依赖注入:容器自动将依赖注入到目标对象
  3. 松耦合:对象之间通过接口依赖,降低耦合度
  4. 易测试:便于单元测试和集成测试
  5. 易维护:集中管理对象,便于配置和修改

最佳实践:

  • 优先使用构造器注入
  • 使用注解配置(@Component、@Service 等)
  • 合理使用 Bean 的作用域
  • 理解 Bean 的生命周期,合理使用初始化和销毁回调
  • 避免循环依赖,必要时使用 @Lazy

Spring IOC 是 Spring 框架的基石,理解它对于掌握 Spring 至关重要!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐