控制反转与容器管理
反射机制
Java 反射机制(Reflection)允许程序在运行时 获取类元信息,核心 API 在 java.lang.reflect 包。
| 类/接口 | 用途 |
|---|---|
Class<T> |
表示类或接口的元数据 |
Field |
表示类的字段(成员变量) |
Method |
表示类的方法 |
Constructor<T> |
表示类的构造方法 |
Modifier |
解析修饰符(public, private, static 等) |
Array |
动态创建和访问数组 |
Parameter |
方法参数信息(Java 8+) |
- 类反射:直接通过类本身(Class 对象)反射到元信息
- 对象反射:通过对象实例,先反射到类本身(Class 对象),再获取其类的元信息
java
class Person {
private String name;
public Person(String name) { this.name = name; }
public String getName() { return name; }
}
// 类反射 - 直接通过类获取元信息
Class<Person> clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
// 对象反射 - 通过实例获取类元信息
Person person = new Person();
Class<?> personClass = person.getClass();
Field nameField = personClass.getDeclaredField("name");
Spring 的高级特性(依赖注入、动态代理)基于反射实现,提供了更高级的反射工具类及 API,诸如 ReflectionUtils、BeanUtils、AnnotationUtils、ResolvableType 等。
控制反转思想
控制反转(Inversion of Control, IoC)是设计思想 ,核心目标是解耦,将对象创建、配置和生命周期的控制权从应用代码转移给外部容器 ,而依赖注入(Dependency Injection, DI)是实现手段,IoC 容器 (框架)则是依赖注入的实施者。
- 传统方式 :对象 A 需要依赖对象 B 时,由 A 内部通过
new B()主动创建和管理 B。 - IoC 方式 :对象 A 仅声明它需要 B。由 IoC 容器 负责在适当的时候创建 B 的实例并"注入"给 A。
Spring IoC 容器的核心接口包括 BeanFactory(根接口)、ApplicationContext(是前者子接口),而"容器"本身就是实现他们的对象,原生 Spring 需要手动创建 IoC 容器实例(显式指定 XML/配置类、手动管理依赖、借助外部 Servlet 容器等),而 Spring Boot 已把过程封装在 SpringApplication.run() 方法中自动创建(自动扫描约定路径、Starter POM 自动管理依赖、内置 Servlet 容器等)。
Spring IoC 容器需要根据配置(XML、注解、Java Config)定义 Bean(内部维护成注册表,可以类比成一个全局哈希表,根据名称映射实例)。
Spring IoC 容器在初始化 Bean、进行依赖注入(如 @Autowired 字段注入)时,底层大量使用了 Field、Method、Constructor 等反射 API获取和分析元数据。
容器启动流程
- JVM 启动:加载应用主类。
- 启动类静态代码块 :执行主类中的
static {}代码块。 - Spring 启动监听 :
SpringApplicationRunListener.starting()被触发,标志 Spring 启动开始。 - 环境准备 :加载配置文件(如
application.yml)、初始化环境变量等。 - 应用上下文初始化 :执行
ApplicationContextInitializer.initialize(),对 Spring 上下文进行早期定制。 - 主方法执行 :启动类的
main()方法被调用。注意:此时 Spring 容器尚未初始化。 - IoC 容器启动 (
SpringApplication.run()) :核心阶段 ,开始加载和装配 Bean。- 容器首先识别启动类(因被
@SpringBootApplication标注,它本身也是一个 Bean)并进行处理。 - 根据组件扫描、配置等,实例化并装配其他所有 Bean。
- 容器首先识别启动类(因被
- Bean 初始化后回调 :在所有 Bean 装配完成后,容器调用所有被
@PostConstruct标记的方法。 - 应用就绪回调 :最后,执行所有
CommandLineRunner或ApplicationRunner的实现,此时应用已完全启动,可以执行业务逻辑。
依赖注入
配置方式
| 方式 | 配置方式 | 说明 |
|---|---|---|
| XML 配置 | <bean> 标签,<property> 或 <constructor-arg> |
传统方式,显式声明 Bean 和依赖关系 |
| Java 配置 | @Configuration + @Bean 方法 |
类型安全,可编程控制创建逻辑 |
| 注解自动装配 | @Component, @Autowired, @ComponentScan |
主流方式,声明式,简洁高效 |
注入方式
属性注入 ,注解于属性,最为简洁,但无法注入 final 属性(因其需要在对象构造完成之前被初始化)
java
@Controller
public class ClassA {
@Autowired
private ClassB classB;
}
构造器注入 ,注解于构造函数,可注入 final 属性,线程安全,方便调试
java
@Component
public class ClassA {
private final ClassB classB;
// 构造器注入
@Autowired // 在Spring 4.3以后,如果只有一个构造器,可以省略@Autowired
public ClassA(ClassB classB) {
this.classB = classB;
}
}
Setter 注入 ,注解于 Setter 函数,无法注入 final 属性,非线程安全
java
@Component
public class ClassA {
private ClassB dependencyB;
// Setter方法注入
@Autowired
public void setDependencyB(ClassB b) {
this.dependencyB = b; // 依赖可以在对象创建后设置
}
}
生命周期
Spring Bean 的生命周期指的是 Bean 从被容器创建、初始化、提供服务,到最终被容器销毁的整个过程 。Spring 容器在这个过程的关键节点 提供了回调机制(Callback Hooks),允许你介入并执行自定义逻辑(如初始化资源、加载配置、释放连接等)。这种管理带来了极大的灵活性和控制力。
| 特性 | 类的生命周期 | Bean 的生命周期 |
|---|---|---|
| 主体 | 类型(Class) | 实例(Bean) |
| 管理者 | JVM 类加载子系统 | Spring IoC 容器 |
| 触发 | 首次主动使用(new、访问静态成员等)或反射加载 | 容器根据配置和依赖关系触发创建 |
| 内存区域 | 方法区(元空间) | 堆内存(对象实例本身) |
| 数量 | 每个加载器下唯一 | 可根据作用域(如 singleton, prototype)有多个 |
| 关键阶段 | 加载 -> 链接 -> 初始化 -> 使用 -> 卸载 | 实例化 -> 属性填充 -> 初始化 -> 使用 -> 销毁 |
| 阶段 | 方法/接口 | 执行时机 | 作用 |
|---|---|---|---|
| 实例化 | 构造器调用 | Bean 创建时 | 创建 Bean 实例 |
| 属性注入 | @Autowired, @Value |
实例化后 | 注入依赖和配置 |
| Aware 接口 | BeanNameAware 等 |
注入完成后 | 获取容器信息 |
| 初始化前 | @PostConstruct |
使用 Bean 前 | 早期初始化逻辑 |
| 初始化 | InitializingBean |
@PostConstruct 后 |
标准初始化逻辑 |
| 初始化后 | BeanPostProcessor |
初始化完成后 | 生成代理对象等 |
| 销毁 | @PreDestroy |
容器关闭时 | 清理资源 |
装配优先级
@Autowired 是Spring 框架提供的注解,@Resource 是 JDK 提供的注解
@Autowired优先级链:类型匹配 →@Qualifier→ 字段名匹配 →@Primary→ 抛异常@Resource优先级链:名称匹配(显式 name)→ 名称匹配(字段名)→ 类型匹配 → 抛异常
@Autowired @Resource 是 否 是 否 是 否 是 否 是 否 是 否 唯一匹配 无或多例 开始依赖注入 使用哪个注解? 按类型匹配 按名称匹配 唯一类型匹配? 直接注入 使用@Qualifier? 按限定名称注入 按字段名匹配? 注入匹配Bean 存在@Primary Bean? 注入@Primary Bean 抛出异常 指定name属性? 按name精确匹配 用字段名匹配 匹配成功? 注入名称匹配Bean 按类型回退匹配? 注入类型匹配Bean 抛出异常 注入成功 注入失败
循环依赖问题
java
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB; // 依赖 ServiceB
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA; // 又依赖 ServiceA,形成循环
}
Spring 自身通过三级缓存解决单例 Bean 的循环依赖
ServiceA实例化 → 放入三级缓存(工厂对象)ServiceA注入ServiceB→ 开始创建ServiceBServiceB注入ServiceA→ 从三级缓存获取早期引用ServiceB完成创建 →ServiceA完成注入
java
// 1. 一级缓存:存放完全初始化好的 Bean(成品)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 2. 二级缓存:存放早期暴露的 Bean(半成品,已实例化但未完成属性注入)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 3. 三级缓存:存放 ObjectFactory,用于创建代理对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
此外,还可以在设计层面解耦
java
// 提取公共逻辑到第三方类
@Service
public class CommonService {} // A和B都依赖C,而非彼此
@Service
public class ServiceA {
@Autowired
private CommonService commonService;
}
接口隔离
java
public interface Processor {
void process();
}
@Service
public class ServiceA implements Processor {
// 依赖接口而非具体实现
}
使用 Setter 注入
java
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired // Setter允许延迟注入
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
应用上下文获取
java
@Service
public class ServiceA implements ApplicationContextAware {
private ApplicationContext context;
public void doSomething() {
ServiceB b = context.getBean(ServiceB.class); // 需要时获取
}
}
使用 @Lazy 延迟注入,直到第一次实际使用时才创建,但不适用于 prototype 作用域的 Bean
java
// 打破构造器循环依赖
@Service
public class ServiceA {
private final ServiceB serviceB;
// 延迟注入解决构造器循环依赖
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// 减少启动开销
@Configuration
public class AppConfig {
@Bean
@Lazy // 首次调用时初始化
public HeavyService heavyService() {
return new HeavyService();
}
}