一、IOC
IOC的全称是 Inversion of Control,中文翻译为 "控制反转",它是一种设计思想,核心是颠覆了传统的对象创建和依赖管理方式。
1.控制反转
"控制"在这里指的是对象的创建权、依赖的管理控制权。
传统方式(无IOC):
开发者在程序中直接 new 对象,手动管理所有依赖。比如你需要一个UserService,它依赖UserDao,你要自己创建:
java
// 传统方式:开发者手动控制对象创建和依赖
public class UserService {
// 手动创建依赖对象
private UserDao userDao = new UserDaoImpl();
public void addUser() {
userDao.add();
}
}
// 使用时还要手动创建UserService
public class Main {
public static void main(String[] args) {
UserService userService = new UserService();
userService.addUser();
}
}
IOC方式(控制反转):
把对象的创建、依赖注入、生命周期管理等"控制权"反转给容器(比如Spring容器),开发者只需要定义"需要什么",而不用管"怎么创建"。
java
// IOC方式:开发者只声明依赖,容器负责创建和注入
public class UserService {
// 声明依赖,不手动创建
private UserDao userDao;
// 提供构造器/setter,让容器注入依赖
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void addUser() {
userDao.add();
}
}
2.DI
DI(Dependency Injection,依赖注入):IOC的实现方式,容器将依赖关系注入到对象中。
核心含义:**"注入"**是指由外部实体(IOC容器)在创建对象时,将其所依赖的对象通过构造函数、Setter方法或接口等方式"传递"(注入)给它。
DI是实现IOC思想的具体技术手段。它明确了"反转"的具体方式:通过"注入"来提供依赖。
3.IOC容器(实现IOC/DI的框架组件)
IOC容器(在 Spring 中就是 ApplicationContext)是具体干活的角色。它的核心工作是:
- 创建和管理对象
- 管理对象的生命周期
- 自动注入依赖
4.IOC的优点
- 解耦:类与类之剑不再直接依赖
- 简化开发:无需手动管理对象的创建和依赖
- 便于测试:可以轻松替换依赖为模拟对象
- 统一管理:容器统一管理对象的生命周期
二、Spring IOC
Spring IOC 是 "IOC 思想" 的Java落地实现,Spring 框架把 IOC 思想变成了可以直接使用的工具(Spring IOC 容器)。
1.配置方式
1.1.XML配置(传统方式)
xml
<!-- applicationContext.xml -->
<beans>
<!-- 定义Bean -->
<bean id="userService" class="com.example.UserServiceImpl">
<!-- 属性注入 -->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.UserDaoImpl"/>
</beans>
1.2.注解配置(推荐)
java
@Component // 标识为Spring组件
public class UserServiceImpl implements UserService {
@Autowired // 自动注入依赖
private UserDao userDao;
public void addUser() {
userDao.add();
}
}
@Repository // 数据访问层组件
public class UserDaoImpl implements UserDao {
// ...
}
1.3.Java配置类
java
@Configuration // 声明为配置类
@ComponentScan("com.example") // 扫描组件
@PropertySource("classpath:app.properties") // 加载配置文件
public class AppConfig {
@Bean // 声明Bean
public UserService userService() {
return new UserServiceImpl(userDao());
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
2.Bean的声明
控制反转是将对象的控制权交给Spring的IOC容器,由IOC容器创建和管理对象,IOC容器创建的对象称为Bean对象。
要把某个对象交给IOC容器管理,需要在类上添加 @Component 注解。
@Component 及其衍生注解:
| 注解 | 说明 | 位置 |
|---|---|---|
| @Component | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
| @Controller | @Component的衍生注解 | 标注在控制层类上 |
| @Service | @Component的衍生注解 | 标注在业务层类上 |
| @Repository | @Component的衍生注解 | 标注在数据访问层类上 |
声明Bean的时候,可以通过注解的value属性指定Bean的名字,如果没有指定,默认为 类名 首字母小写。
3.组件扫描
通过注解声明的Bean想要生效,还需要被组件扫描注解 @ComponentScan 扫描。@ComponentScan 注解主要用于启动类(@SpringBootApplication)、配置类(@Configuration)。
隐式使用
当使用 @SpringBootApplication 注解时,它已经包含了 @ComponentScan 注解,默认会扫描主类所在包及其子包:
java
// 等价于:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {
// ...
}
显示指定扫描路径
如果组件不在默认包下,需要显示指定:
java
@ComponentScan(basePackages = {
"com.example.service",
"com.example.repository",
"com.example.controller"
})
4.依赖注入
依赖注入的常见方式,有以下三种:
4.1.构造器注入(推荐)
java
@Service
public class UserService {
private final UserDao userDao;
// @Autowired 可省略(Spring 4.3+)
public UserService(UserDao userDao) {
this.userDao= userDao;
}
}
- 优点:能清晰地看到类的依赖关系、提高了代码的安全性。
- 缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿。
4.2.Setter注入
java
@Service
public class UserService {
private UserDao userDao;
@Autowired
public void setUserService(UserDao userDao) {
this.userDao= userDao;
}
}
- 优点:保持了类的封装性,依赖关系更清晰。
- 缺点:需要额外编写setter方法,增加了代码量。
4.3.属性注入(不推荐,测试时使用)
java
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
- 优点:代码简洁、方便快速开发。
- 缺点:隐藏了类之间的依赖关系、可能会破坏类的封装性。
5.Bean的作用域
java
@Component
@Scope("singleton") // 默认,单例模式,整个容器中只有一个实例
public class SingletonBean { }
@Component
@Scope("prototype") // 原型模式,每次获取都创建新实例
public class PrototypeBean { }
@Component
@Scope("request") // 每个HTTP请求一个实例
public class RequestBean { }
@Component
@Scope("session") // 每个HTTP Session一个实例
public class SessionBean { }