什么是 Spring IOC?
·
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 的核心价值:
- 控制反转:将对象创建和依赖管理的控制权交给容器
- 依赖注入:容器自动将依赖注入到目标对象
- 松耦合:对象之间通过接口依赖,降低耦合度
- 易测试:便于单元测试和集成测试
- 易维护:集中管理对象,便于配置和修改
最佳实践:
- 优先使用构造器注入
- 使用注解配置(@Component、@Service 等)
- 合理使用 Bean 的作用域
- 理解 Bean 的生命周期,合理使用初始化和销毁回调
- 避免循环依赖,必要时使用 @Lazy
Spring IOC 是 Spring 框架的基石,理解它对于掌握 Spring 至关重要!
更多推荐



所有评论(0)