Spring IOC(控制反转)
定位说明
Spring 系列第 1 篇(九部分系统讲解,1660 行)。核心回答:对象为什么交给容器创建——从"问题的提出"到 BeanFactory/ApplicationContext 容器组件、实际应用、与反射的结合、性能与最佳实践。前置:Class 对象与反射机制。
第一部分:问题的提出
一、传统方式的困境
想象一个场景:
// 传统方式:手动创建对象和管理依赖
public class OrderService {
private OrderRepository orderRepository;
private UserService userService;
private PaymentService paymentService;
public OrderService() {
// 需要手动创建所有依赖
this.orderRepository = new OrderRepository();
this.userService = new UserService();
this.paymentService = new PaymentService();
}
public void createOrder(Order order) {
// 业务逻辑
userService.validateUser(order.getUserId());
orderRepository.save(order);
paymentService.processPayment(order);
}
}
// 使用时也要手动创建
OrderService orderService = new OrderService();
orderService.createOrder(order);
这种方式有什么问题?
- 耦合度高 - 直接依赖具体实现类,修改实现需要改动多处代码
- 难以扩展 - 要替换实现类需要修改构造函数
- 难以测试 - 单元测试无法mock依赖
- 代码冗长 - 重复的对象创建代码
- 依赖链复杂 - 如果OrderRepository还有其他依赖,会形成复杂的依赖链
OrderService
├─ OrderRepository
│ ├─ Database
│ └─ Logger
├─ UserService
│ ├─ UserRepository
│ └─ Cache
└─ PaymentService
├─ PaymentGateway
└─ Logger
二、理想的解决方案
我们希望能这样写:
// 理想方式:只声明依赖,框架自动处理
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
public void createOrder(Order order) {
// 只关注业务逻辑
userService.validateUser(order.getUserId());
orderRepository.save(order);
paymentService.processPayment(order);
}
}
// 使用时直接注入,无需创建
@Autowired
private OrderService orderService;
orderService.createOrder(order); // 直接使用
这就是IOC要解决的问题:让框架自动管理对象的创建和依赖注入。
第二部分:IOC的核心概念
三、什么是IOC?
3.1 IOC的定义
IOC(Inversion of Control,控制反转)是一种设计思想,核心是把对象的创建权和依赖管理权从代码中交给框架容器来管理。
┌──────────────────────────────────────────┐
│ IOC的本质 │
├──────────────────────────────────────────┤
│ │
│ 传统方式: │
│ ├─ 程序员手动创建对象 │
│ ├─ 程序员手动管理依赖 │
│ └─ 程序员控制对象的生命周期 │
│ │
│ IOC方式: │
│ ├─ 框架自动创建对象 │
│ ├─ 框架自动管理依赖 │
│ └─ 框架控制对象的生命周期 │
│ │
│ 控制反转: │
│ └─ 控制权从程序员手里反转到框架手里 │
│ │
└──────────────────────────────────────────┘
3.2 IOC与GC的类比
┌──────────────────────────────────────────┐
│ IOC与GC的思想对比 │
├──────────────────────────────────────────┤
│ │
│ 垃圾回收(GC) │
│ ├─ 问题:什么时候释放对象 │
│ ├─ 解决:自动回收不用的对象 │
│ └─ 效果:程序员不用手动释放内存 │
│ │
│ 控制反转(IOC) │
│ ├─ 问题:什么时候创建对象、怎么注入依赖 │
│ ├─ 解决:框架自动创建和注入 │
│ └─ 效果:程序员不用手动创建对象 │
│ │
│ 共同点: │
│ ├─ 都是"自动化"思想 │
│ ├─ 都是"把细节交给框架" │
│ └─ 都是"程序员专注业务逻辑" │
│ │
└──────────────────────────────────────────┘
3.3 IOC的核心优势
┌──────────────────────────────────────────┐
│ IOC的核心优势 │
├──────────────────────────────────────────┤
│ │
│ 1. 解耦 │
│ ├─ 依赖接口而不是实现 │
│ ├─ 实现类可以随意替换 │
│ └─ 修改不会影响调用方 │
│ │
│ 2. 灵活性 │
│ ├─ 通过配置选择不同实现 │
│ ├─ 支持条件装配 │
│ └─ 支持动态代理和增强 │
│ │
│ 3. 可测试性 │
│ ├─ 容易mock依赖 │
│ ├─ 支持单元测试 │
│ └─ 支持集成测试 │
│ │
│ 4. 自动化 │
│ ├─ 自动创建对象 │
│ ├─ 自动注入依赖 │
│ └─ 自动管理生命周期 │
│ │
└──────────────────────────────────────────┘
第三部分:IOC的实现原理
四、Spring IOC的四个阶段
4.1 阶段概览
┌──────────────────────────────────────────┐
│ Spring IOC的四个阶段 │
├──────────────────────────────────────────┤
│ │
│ 第1阶段:容器启动 │
│ ├─ 加载配置文件/配置类 │
│ ├─ 创建IOC容器实例 │
│ └─ 准备开始管理Bean │
│ │
│ 第2阶段:Bean定义注册 │
│ ├─ 扫描类路径 │
│ ├─ 读取注解或XML配置 │
│ ├─ 创建BeanDefinition │
│ └─ 注册到容器 │
│ │
│ 第3阶段:实例化和依赖注入 │
│ ├─ 通过反射创建Bean实例 │
│ ├─ 递归注入依赖 │
│ ├─ 支持多种注入方式 │
│ └─ 处理循环依赖 │
│ │
│ 第4阶段:初始化Bean │
│ ├─ 执行@PostConstruct方法 │
│ ├─ 执行InitializingBean接口 │
│ ├─ 执行BeanPostProcessor │
│ └─ Bean准备就绪 │
│ │
└──────────────────────────────────────────┘
4.2 第一阶段:容器启动
// 容器启动的三种方式
// 方式1:基于XML配置
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 方式2:基于注解配置类
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 方式3:Spring Boot自动启动
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 容器启动的工作流程
public class ContainerStartupDemo {
public static void main(String[] args) {
// 1. 加载配置
// 2. 创建BeanFactory
// 3. 创建ApplicationContext
// 4. 准备开始扫描和注册Bean
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 5. 容器启动完成,可以获取Bean
UserService userService = context.getBean(UserService.class);
}
}
4.3 第二阶段:Bean定义注册
// Bean定义注册的过程
// 1. 定义Bean
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
}
@Component
public class UserRepository {
// ...
}
// 2. Spring扫描注解
// ClassPathBeanDefinitionScanner会扫描@Component及其衍生注解
// 3. 创建BeanDefinition
// BeanDefinition是Bean的"说明书",包含:
public class BeanDefinitionDemo {
// BeanDefinition包含的信息:
// - 类名:com.example.UserService
// - 作用域:singleton(单例)或prototype(原型)
// - 是否懒加载:false
// - 初始化方法:@PostConstruct标注的方法
// - 销毁方法:@PreDestroy标注的方法
// - 依赖信息:@Autowired标注的字段
// - 其他元数据
}
// 4. 注册到容器
// BeanDefinitionRegistry会把BeanDefinition注册到容器中
BeanDefinition的结构:
public interface BeanDefinition {
// 获取Bean的类名
String getBeanClassName();
// 获取作用域(singleton或prototype)
String getScope();
// 是否是单例
boolean isSingleton();
// 是否是原型
boolean isPrototype();
// 是否懒加载
boolean isLazyInit();
// 获取初始化方法名
String getInitMethodName();
// 获取销毁方法名
String getDestroyMethodName();
// 获取构造函数参数
ConstructorArgumentValues getConstructorArgumentValues();
// 获取属性值
MutablePropertyValues getPropertyValues();
}
4.4 第三阶段:实例化和依赖注入
// 实例化和依赖注入的过程
public class BeanInstantiationDemo {
// 假设有这样的Bean
@Component
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserService userService;
}
// Spring的处理流程:
// 1. 获取OrderService的BeanDefinition
BeanDefinition beanDef = beanFactory.getBeanDefinition("orderService");
// 2. 通过反射创建实例
Class<?> beanClass = Class.forName(beanDef.getBeanClassName());
OrderService orderService = beanClass.getDeclaredConstructor().newInstance();
// 3. 检查依赖
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
// 4. 递归创建依赖的Bean
Object dependency = beanFactory.getBean(field.getType());
// 5. 注入依赖
field.setAccessible(true);
field.set(orderService, dependency);
}
}
// 6. 返回完整注入的Bean
return orderService;
}
三种注入方式:
// 1. 字段注入(最常用)
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
}
// 2. 构造器注入(推荐,便于测试)
@Component
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 3. Setter注入(不推荐)
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
处理循环依赖:
// 循环依赖的问题
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB; // A依赖B
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA; // B依赖A
}
// Spring的解决方案:三级缓存
public class CircularDependencyDemo {
// 第一级缓存:完整的Bean
private Map<String, Object> singletonObjects = new HashMap<>();
// 第二级缓存:半成品Bean(已创建但未注入)
private Map<String, Object> earlySingletonObjects = new HashMap<>();
// 第三级缓存:Bean工厂
private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
// 解决流程:
// 1. 创建ServiceA实例,放入第二级缓存
// 2. ServiceA需要ServiceB,开始创建ServiceB
// 3. 创建ServiceB实例,放入第二级缓存
// 4. ServiceB需要ServiceA,从第二级缓存获取(已存在)
// 5. ServiceB注入完成,放入第一级缓存
// 6. ServiceA注入完成,放入第一级缓存
}
4.5 第四阶段:初始化Bean
// Bean初始化的过程
@Component
public class UserService implements InitializingBean {
@Autowired
private UserRepository userRepository;
// 1. 执行@PostConstruct方法
@PostConstruct
public void init() {
System.out.println("1. @PostConstruct方法执行");
// 初始化逻辑
}
// 2. 执行InitializingBean接口的afterPropertiesSet方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("2. InitializingBean.afterPropertiesSet()执行");
// 初始化逻辑
}
// 3. 执行initMethod(在@Bean中指定)
public void customInit() {
System.out.println("3. initMethod执行");
// 初始化逻辑
}
}
// 在配置类中指定initMethod
@Configuration
public class AppConfig {
@Bean(initMethod = "customInit")
public UserService userService() {
return new UserService();
}
}
// 4. BeanPostProcessor的前置和后置方法
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("4. BeanPostProcessor.postProcessBeforeInitialization()");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("5. BeanPostProcessor.postProcessAfterInitialization()");
// 这里可以进行代理增强
return bean;
}
}
// 执行顺序:
// 1. @PostConstruct
// 2. InitializingBean.afterPropertiesSet()
// 3. initMethod
// 4. BeanPostProcessor.postProcessBeforeInitialization()
// 5. BeanPostProcessor.postProcessAfterInitialization()
第四部分:IOC容器的核心组件
五、BeanFactory与ApplicationContext
5.1 两者的关系
┌──────────────────────────────────────────┐
│ BeanFactory与ApplicationContext │
├──────────────────────────────────────────┤
│ │
│ BeanFactory(低级容器) │
│ ├─ 职责:创建和管理Bean │
│ ├─ 功能:基础的Bean生命周期管理 │
│ ├─ 性能:轻量级 │
│ └─ 用途:底层框架 │
│ │
│ ApplicationContext(高级容器) │
│ ├─ 继承:extends BeanFactory │
│ ├─ 职责:提供更多企业级功能 │
│ ├─ 功能: │
│ │ ├─ Bean生命周期管理 │
│ │ ├─ 国际化支持 │
│ │ ├─ 事件发布 │
│ │ ├─ 资源加载 │
│ │ └─ AOP支持 │
│ ├─ 性能:相对较重 │
│ └─ 用途:应用程序 │
│ │
└──────────────────────────────────────────┘
5.2 ApplicationContext的实现类
// 1. ClassPathXmlApplicationContext - 从classpath加载XML配置
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. FileSystemXmlApplicationContext - 从文件系统加载XML配置
ApplicationContext context = new FileSystemXmlApplicationContext("/path/to/applicationContext.xml");
// 3. AnnotationConfigApplicationContext - 从注解配置类加载
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 4. AnnotationConfigWebApplicationContext - Web应用的注解配置
// 在Spring MVC中使用
// 5. WebApplicationContext - Web应用的容器
// 通过DispatcherServlet创建
5.3 BeanFactory的工作原理
public class BeanFactoryDemo {
public static void main(String[] args) {
// 1. 创建BeanFactory
BeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. 注册BeanDefinition
BeanDefinition beanDef = new RootBeanDefinition(UserService.class);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("userService", beanDef);
// 3. 获取Bean(延迟初始化)
UserService userService = beanFactory.getBean("userService", UserService.class);
// BeanFactory特点:
// - 延迟初始化:getBean时才创建
// - 轻量级:只管理Bean的创建
// - 不支持国际化、事件等高级功能
}
}
第五部分:IOC的实际应用
六、Spring中的IOC应用
6.1 组件扫描和自动装配
// 1. 定义配置类
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
// 2. 定义Bean
@Component
public class UserRepository {
public User findById(int id) {
return new User(id, "Tom");
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User getUser(int id) {
return userRepository.findById(id);
}
}
@Controller
public class UserController {
@Autowired
private UserService userService;
public void handleRequest(int id) {
User user = userService.getUser(id);
}
}
// 3. 启动容器
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 4. 获取Bean
UserController controller = context.getBean(UserController.class);
controller.handleRequest(1);
}
}
// Spring的处理流程:
// 1. 扫描@ComponentScan指定的包
// 2. 找到@Component及其衍生注解(@Service、@Controller等)
// 3. 为每个类创建BeanDefinition
// 4. 创建Bean实例
// 5. 通过@Autowired注入依赖
// 6. 执行初始化方法
// 7. Bean准备就绪
6.2 条件装配
// 根据条件选择不同的Bean实现
// 1. 定义接口
public interface DataSource {
Connection getConnection();
}
// 2. 定义多个实现
@Component
@ConditionalOnProperty(name = "db.type", havingValue = "mysql")
public class MysqlDataSource implements DataSource {
@Override
public Connection getConnection() {
return new MysqlConnection();
}
}
@Component
@ConditionalOnProperty(name = "db.type", havingValue = "postgresql")
public class PostgresqlDataSource implements DataSource {
@Override
public Connection getConnection() {
return new PostgresqlConnection();
}
}
// 3. 注入时自动选择
@Service
public class UserService {
@Autowired
private DataSource dataSource; // 根据配置自动选择实现
}
// 4. 配置文件
// application.properties
// db.type=mysql
6.3 Bean的作用域
// 1. Singleton(单例)- 默认
@Component
@Scope("singleton")
public class SingletonBean {
// 整个应用只有一个实例
}
// 2. Prototype(原型)
@Component
@Scope("prototype")
public class PrototypeBean {
// 每次获取都创建新实例
}
// 3. Request(请求作用域)- Web应用
@Component
@Scope("request")
public class RequestBean {
// 每个HTTP请求创建一个实例
}
// 4. Session(会话作用域)- Web应用
@Component
@Scope("session")
public class SessionBean {
// 每个HTTP会话创建一个实例
}
// 验证作用域
public class ScopeDemo {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// Singleton验证
SingletonBean bean1 = context.getBean(SingletonBean.class);
SingletonBean bean2 = context.getBean(SingletonBean.class);
System.out.println(bean1 == bean2); // true,同一个实例
// Prototype验证
PrototypeBean bean3 = context.getBean(PrototypeBean.class);
PrototypeBean bean4 = context.getBean(PrototypeBean.class);
System.out.println(bean3 == bean4); // false,不同实例
}
}
6.4 Bean的生命周期管理
// Bean的完整生命周期
@Component
public class LifecycleBean implements InitializingBean, DisposableBean {
private String name;
// 1. 构造函数
public LifecycleBean() {
System.out.println("1. 构造函数执行");
}
// 2. 属性注入(通过反射)
public void setName(String name) {
System.out.println("2. 属性注入");
this.name = name;
}
// 3. BeanNameAware接口
@Override
public void setBeanName(String name) {
System.out.println("3. setBeanName()执行");
}
// 4. BeanFactoryAware接口
@Override
public void setBeanFactory(BeanFactory beanFactory) {
System.out.println("4. setBeanFactory()执行");
}
// 5. ApplicationContextAware接口
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
System.out.println("5. setApplicationContext()执行");
}
// 6. BeanPostProcessor的前置方法
// 由Spring框架调用
// 7. @PostConstruct注解
@PostConstruct
public void init() {
System.out.println("7. @PostConstruct执行");
}
// 8. InitializingBean接口
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("8. InitializingBean.afterPropertiesSet()执行");
}
// 9. BeanPostProcessor的后置方法
// 由Spring框架调用
// 10. 正常使用
public void doSomething() {
System.out.println("10. Bean正常使用");
}
// 11. @PreDestroy注解
@PreDestroy
public void cleanup() {
System.out.println("11. @PreDestroy执行");
}
// 12. DisposableBean接口
@Override
public void destroy() throws Exception {
System.out.println("12. DisposableBean.destroy()执行");
}
}
// 执行顺序:
// 1. 构造函数
// 2. 属性注入
// 3. setBeanName()
// 4. setBeanFactory()
// 5. setApplicationContext()
// 6. BeanPostProcessor.postProcessBeforeInitialization()
// 7. @PostConstruct
// 8. InitializingBean.afterPropertiesSet()
// 9. BeanPostProcessor.postProcessAfterInitialization()
// 10. Bean正常使用
// 11. @PreDestroy
// 12. DisposableBean.destroy()
第六部分:IOC与其他机制的结合
七、IOC与AOP的结合
// IOC创建Bean,AOP为Bean增强
// 1. 定义业务接口
public interface UserService {
void save(User user);
}
// 2. 实现业务接口
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public void save(User user) {
userRepository.save(user);
}
}
// 3. 定义切面
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法调用前:" + joinPoint.getSignature());
}
@After("execution(* com.example.service.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("方法调用后:" + joinPoint.getSignature());
}
}
// 4. IOC和AOP的协作
public class IOCAndAOPDemo {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// IOC创建Bean
UserService userService = context.getBean(UserService.class);
// 实际上获取的是代理对象
System.out.println(userService.getClass().getName());
// 输出:com.sun.proxy.$Proxy0(JDK动态代理)
// 或:com.example.UserServiceImpl$$EnhancerByCGLIB$$xxx(CGLIB代理)
// 调用方法时,AOP会织入增强逻辑
userService.save(new User());
// 输出:
// 方法调用前:...
// 保存用户
// 方法调用后:...
}
}
// IOC和AOP的工作流程:
// 1. IOC扫描@Service注解,创建UserServiceImpl的BeanDefinition
// 2. IOC创建UserServiceImpl实例
// 3. IOC注入依赖
// 4. AOP检测到@Transactional注解
// 5. AOP创建代理对象,包装UserServiceImpl
// 6. 将代理对象放入容器
// 7. 调用代理对象的方法时,AOP织入增强逻辑
八、IOC与注解的结合
// 注解驱动的IOC
// 1. 元注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
String value() default "";
}
// 2. 自定义注解处理器
@Component
public class MyServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 扫描@MyService注解
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
return metadataReader.getClassMetadata()
.hasAnnotation(MyService.class.getName());
});
// 注册BeanDefinition
scanner.scan("com.example");
}
}
// 3. 使用自定义注解
@MyService("userService")
public class UserService {
public void save(User user) {
System.out.println("保存用户");
}
}
// 4. 注解驱动的依赖注入
@MyService("orderService")
public class OrderService {
@Autowired
private UserService userService;
public void createOrder(Order order) {
userService.save(order.getUser());
}
}
// Spring通过反射读取注解,自动完成Bean的创建和注入
第七部分:IOC的性能与最佳实践
九、IOC的性能考虑
9.1 启动性能
// IOC容器启动的性能开销
public class StartupPerformanceDemo {
public static void main(String[] args) {
// 测试启动时间
long startTime = System.currentTimeMillis();
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
long endTime = System.currentTimeMillis();
System.out.println("容器启动耗时:" + (endTime - startTime) + "ms");
// 性能开销来自:
// 1. 类扫描 - 扫描classpath中的所有类
// 2. 注解解析 - 通过反射读取注解信息
// 3. BeanDefinition创建 - 为每个Bean创建定义
// 4. Bean实例化 - 通过反射创建实例
// 5. 依赖注入 - 递归注入所有依赖
// 6. 初始化方法执行 - 执行@PostConstruct等
}
}
9.2 性能优化建议
// 1. 使用懒加载
@Component
@Lazy
public class HeavyBean {
// 只在第一次使用时才创建
}
// 2. 缩小扫描范围
@Configuration
@ComponentScan(basePackages = "com.example.service") // 只扫描service包
public class AppConfig {
}
// 3. 使用@Bean而不是@Component
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
// 显式创建,避免注解扫描
return new UserService();
}
}
// 4. 避免循环依赖
// 循环依赖会增加初始化时间
// 应该重新设计架构避免循环依赖
// 5. 使用条件装配减少Bean数量
@Component
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class OptionalBean {
// 只在特定条件下创建
}
// 6. 使用Spring Boot的自动配置
// Spring Boot已经优化了启动性能
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
十、IOC的最佳实践
10.1 推荐做法
// 1. 优先使用构造器注入
@Service
public class UserService {
private final UserRepository userRepository;
private final UserValidator userValidator;
// 构造器注入的优势:
// - 依赖不可变
// - 便于测试
// - 清晰表达依赖关系
public UserService(UserRepository userRepository, UserValidator userValidator) {
this.userRepository = userRepository;
this.userValidator = userValidator;
}
}
// 2. 依赖接口而不是实现
@Service
public class OrderService {
private final PaymentService paymentService; // 依赖接口
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
// 3. 使用@Qualifier消除歧义
@Service
public class OrderService {
@Autowired
@Qualifier("alipayService")
private PaymentService paymentService;
}
// 4. 为Bean提供有意义的名称
@Service("userService") // 明确指定Bean名称
public class UserServiceImpl implements UserService {
}
// 5. 使用配置类而不是XML
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepositoryImpl();
}
@Bean
public UserService userService(UserRepository userRepository) {
return new UserServiceImpl(userRepository);
}
}
// 6. 合理使用作用域
@Service
@Scope("singleton") // 大多数情况下使用单例
public class UserService {
}
// 7. 实现接口便于测试和扩展
public interface UserService {
void save(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
}
}
// 8. 使用@Autowired(required = false)处理可选依赖
@Service
public class UserService {
@Autowired(required = false)
private UserCache userCache; // 可选依赖
}
// 9. 避免在Bean中使用静态变量
// ❌ 不好的做法
@Service
public class UserService {
private static UserRepository userRepository; // 静态变量
}
// ✅ 好的做法
@Service
public class UserService {
private final UserRepository userRepository; // 实例变量
}
// 10. 使用@ConfigurationProperties进行配置
@Component
@ConfigurationProperties(prefix = "app.user")
public class UserProperties {
private String defaultName;
private int defaultAge;
// getter/setter
}
10.2 避免做法
// ❌ 不要使用字段注入
@Service
public class BadUserService {
@Autowired
private UserRepository userRepository; // 字段注入
// 问题:
// 1. 依赖隐藏,不清楚需要什么
// 2. 难以测试,无法通过构造器传入mock对象
// 3. 可能为null,容易出现NPE
// 4. 依赖可变,容易被修改
}
// ❌ 不要在Bean中创建其他Bean
@Service
public class BadOrderService {
public void createOrder(Order order) {
// ❌ 不要这样做
UserService userService = new UserService();
userService.save(order.getUser());
}
}
// ✅ 应该注入
@Service
public class GoodOrderService {
private final UserService userService;
public GoodOrderService(UserService userService) {
this.userService = userService;
}
public void createOrder(Order order) {
userService.save(order.getUser());
}
}
// ❌ 不要在Bean中使用ApplicationContext.getBean()
@Service
public class BadService {
@Autowired
private ApplicationContext applicationContext;
public void doSomething() {
// ❌ 不要这样做
UserService userService = applicationContext.getBean(UserService.class);
}
}
// ✅ 应该直接注入
@Service
public class GoodService {
private final UserService userService;
public GoodService(UserService userService) {
this.userService = userService;
}
}
// ❌ 不要创建过多的单例Bean
@Configuration
public class BadConfig {
@Bean
public Bean1 bean1() { return new Bean1(); }
@Bean
public Bean2 bean2() { return new Bean2(); }
// ... 创建100个Bean
// 问题:启动慢,内存占用大
}
// ✅ 应该按需创建
@Configuration
public class GoodConfig {
@Bean
@ConditionalOnProperty(name = "feature.enabled")
public OptionalBean optionalBean() {
return new OptionalBean();
}
}
// ❌ 不要在Bean中做重操作
@Component
public class BadBean {
public BadBean() {
// ❌ 不要在构造函数中做重操作
loadLargeFile();
queryDatabase();
}
}
// ✅ 应该在初始化方法中做
@Component
public class GoodBean {
@PostConstruct
public void init() {
// ✅ 在@PostConstruct中做重操作
loadLargeFile();
queryDatabase();
}
}
// ❌ 不要创建循环依赖
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB; // A依赖B
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA; // B依赖A
}
// ✅ 应该重新设计避免循环依赖
@Service
public class ServiceA {
@Autowired
private ServiceC serviceC;
}
@Service
public class ServiceB {
@Autowired
private ServiceC serviceC;
}
@Service
public class ServiceC {
// 不依赖A和B
}
第八部分:IOC的高级特性
十一、Bean的条件装配
// 根据条件选择不同的Bean
// 1. @ConditionalOnProperty - 根据配置属性
@Component
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public class CacheService {
// 只在cache.enabled=true时创建
}
// 2. @ConditionalOnClass - 根据类是否存在
@Component
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
public class RedisService {
// 只在Redis客户端库存在时创建
}
// 3. @ConditionalOnMissingBean - 根据Bean是否存在
@Configuration
public class AppConfig {
@Bean
@ConditionalOnMissingBean
public UserRepository userRepository() {
return new DefaultUserRepository();
}
}
// 4. @ConditionalOnExpression - 根据SpEL表达式
@Component
@ConditionalOnExpression("${feature.enabled:false}")
public class FeatureService {
// 只在表达式为true时创建
}
// 5. 自定义Condition
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 自定义条件逻辑
return context.getEnvironment().getProperty("my.feature").equals("enabled");
}
}
@Component
@Conditional(MyCondition.class)
public class MyService {
// 只在自定义条件满足时创建
}
十二、Bean的事件机制
// Spring IOC支持事件发布和监听
// 1. 定义事件
public class UserCreatedEvent extends ApplicationEvent {
private final User user;
public UserCreatedEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return user;
}
}
// 2. 发布事件
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createUser(User user) {
// 保存用户
userRepository.save(user);
// 发布事件
eventPublisher.publishEvent(new UserCreatedEvent(this, user));
}
}
// 3. 监听事件
@Component
public class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {
@Override
public void onApplicationEvent(UserCreatedEvent event) {
User user = event.getUser();
System.out.println("用户创建事件:" + user.getName());
// 发送邮件、记录日志等
}
}
// 4. 使用@EventListener注解
@Component
public class UserEventHandler {
@EventListener
public void handleUserCreated(UserCreatedEvent event) {
System.out.println("用户创建:" + event.getUser().getName());
}
@EventListener
public void handleUserDeleted(UserDeletedEvent event) {
System.out.println("用户删除:" + event.getUser().getName());
}
}
十三、Bean的后处理器
// BeanPostProcessor用于在Bean初始化前后进行处理
// 1. 实现BeanPostProcessor接口
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化前:" + beanName);
// 可以在这里修改Bean
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("初始化后:" + beanName);
// 可以在这里创建代理
if (bean instanceof UserService) {
return createProxy(bean);
}
return bean;
}
private Object createProxy(Object bean) {
// 创建代理对象
return Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("代理方法:" + method.getName());
return method.invoke(bean, args);
}
);
}
}
// 2. BeanFactoryPostProcessor - 处理BeanDefinition
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 可以在这里修改BeanDefinition
BeanDefinition beanDef = beanFactory.getBeanDefinition("userService");
beanDef.setScope("prototype"); // 改为原型作用域
}
}
第九部分:总结
十四、IOC的本质思考
IOC(控制反转)是一种设计思想,通过将对象的创建权和依赖管理权交给框架容器,实现了代码的解耦、灵活性和可维护性的提升。
┌──────────────────────────────────────────┐
│ IOC的核心价值 │
├──────────────────────────────────────────┤
│ │
│ 1. 解耦 │
│ ├─ 依赖接口而不是实现 │
│ ├─ 实现类可以随意替换 │
│ └─ 修改不会影响调用方 │
│ │
│ 2. 灵活性 │
│ ├─ 通过配置选择不同实现 │
│ ├─ 支持条件装配 │
│ └─ 支持动态代理和增强 │
│ │
│ 3. 可测试性 │
│ ├─ 容易mock依赖 │
│ ├─ 支持单元测试 │
│ └─ 支持集成测试 │
│ │
│ 4. 自动化 │
│ ├─ 自动创建对象 │
│ ├─ 自动注入依赖 │
│ └─ 自动管理生命周期 │
│ │
│ 5. 可维护性 │
│ ├─ 配置集中管理 │
│ ├─ 易于扩展 │
│ └─ 代码更清晰 │
│ │
└──────────────────────────────────────────┘
十五、IOC的四个阶段总结
┌──────────────────────────────────────────┐
│ IOC的四个阶段总结 │
├──────────────────────────────────────────┤
│ │
│ 第1阶段:容器启动 │
│ ├─ 加载配置文件/配置类 │
│ ├─ 创建IOC容器实例 │
│ └─ 准备开始管理Bean │
│ │
│ 第2阶段:Bean定义注册 │
│ ├─ 扫描类路径 │
│ ├─ 读取注解或XML配置 │
│ ├─ 创建BeanDefinition │
│ └─ 注册到容器 │
│ │
│ 第3阶段:实例化和依赖注入 │
│ ├─ 通过反射创建Bean实例 │
│ ├─ 递归注入依赖 │
│ ├─ 支持多种注入方式 │
│ └─ 处理循环依赖 │
│ │
│ 第4阶段:初始化Bean │
│ ├─ 执行@PostConstruct方法 │
│ ├─ 执行InitializingBean接口 │
│ ├─ 执行BeanPostProcessor │
│ └─ Bean准备就绪 │
│ │
└──────────────────────────────────────────┘
十六、IOC与反射、动态代理的关系
┌──────────────────────────────────────────┐
│ IOC、反射、动态代理的关系 │
├──────────────────────────────────────────┤
│ │
│ 反射(Reflection) │
│ ├─ 本质:运行时获取类信息 │
│ ├─ 作用:IOC的基础 │
│ └─ 用途:创建对象、注入依赖 │
│ │
│ IOC(Inversion of Control) │
│ ├─ 本质:控制权反转 │
│ ├─ 作用:自动创建和管理Bean │
│ └─ 用途:解耦、灵活、可维护 │
│ │
│ 动态代理(Dynamic Proxy) │
│ ├─ 本质:运行时生成代理类 │
│ ├─ 作用:AOP的基础 │
│ └─ 用途:织入增强逻辑 │
│ │
│ 三者的协作: │
│ ├─ 反射:IOC用反射创建对象 │
│ ├─ IOC:创建Bean并注入依赖 │
│ ├─ 动态代理:为Bean创建代理 │
│ └─ AOP:通过代理织入增强逻辑 │
│ │
└──────────────────────────────────────────┘
十七、关键要点回顾
17.1 IOC的核心概念
| 概念 | 说明 | 用途 |
|---|---|---|
| BeanFactory | 低级容器 | 基础Bean管理 |
| ApplicationContext | 高级容器 | 企业级应用 |
| BeanDefinition | Bean的定义 | 描述Bean的元信息 |
| Bean | 容器管理的对象 | 应用中的业务对象 |
| 依赖注入 | 自动注入依赖 | 解耦和灵活 |
| 作用域 | Bean的生命周期 | singleton/prototype等 |
17.2 三种注入方式对比
| 方式 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| 构造器注入 | 依赖清晰、不可变、易测试 | 参数多时复杂 | ⭐⭐⭐⭐⭐ |
| 字段注入 | 简洁 | 隐藏依赖、难测试、可变 | ⭐ |
| Setter注入 | 灵活 | 依赖可变、不清晰 | ⭐⭐ |
17.3 Bean的生命周期
构造函数 → 属性注入 → Aware接口 → BeanPostProcessor前置
→ @PostConstruct → InitializingBean → BeanPostProcessor后置
→ 正常使用 → @PreDestroy → DisposableBean
十八、记忆口诀
IOC是思想,容器来管理
Bean的创建,依赖的注入
四个阶段走,启动注册实例化
初始化完成,Bean就准备好
反射是基础,动态代理增强
AOP织入逻辑,框架自动完成
程序员声明,框架来实现
构造器注入最推荐,字段注入要避免
依赖接口不实现,灵活扩展最关键
单例默认用,原型按需要
BeanFactory轻量级,ApplicationContext功能全
启动会慢点,但好处多得很
解耦灵活可扩展,可维护性最关键
十九、相关链接
- 01-Class对象与反射机制 - IOC的基础
- 动态代理 - AOP的基础
- 03-注解原理 - 注解驱动IOC
- Spring AOP(面向切面编程) - IOC与AOP的结合
二十、常见问题解答
Q1: IOC和DI有什么区别?
答:
- IOC(控制反转) - 是一种设计思想,强调控制权的反转
- DI(依赖注入) - 是实现IOC的一种方式,强调依赖的注入
- 关系:IOC是目标,DI是手段
Q2: 为什么Spring默认使用单例而不是原型?
答:
- 单例性能更好,避免频繁创建对象
- 单例内存占用少
- 大多数Bean是无状态的,可以安全共享
- 如果Bean有状态,应该使用原型或其他作用域
Q3: 循环依赖为什么会发生?
答:
A依赖B,B依赖A
或者更复杂的链式依赖:A→B→C→A
Q4: Spring如何解决循环依赖?
答:
- 使用三级缓存机制
- 第一级:完整的Bean
- 第二级:半成品Bean(已创建但未注入)
- 第三级:Bean工厂
Q5: 为什么推荐使用构造器注入?
答:
- 依赖清晰,一目了然
- 依赖不可变,线程安全
- 便于单元测试
- 强制依赖必须提供
Q6: @Autowired和@Resource有什么区别?
答:
- @Autowired - Spring提供,按类型注入
- @Resource - Java标准,按名称注入
- 推荐使用@Autowired
⬅️ 00-Spring总览 🏠 00-Java ➡️ 依赖注入(DI)
💬 评论