layout | title | date | categories | tags | series | series_index | comments | copyrights |
---|---|---|---|---|---|---|---|---|
post |
IoC |
2024-02-01 00:00:00 +0800 |
编程 |
java spring |
深入 Spring 源码 |
1 |
true |
原创 |
Spring 是一个开源的轻量级 JavaEE 框架。它的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 的 IoC 容器负责管理 JavaBean 的生命周期,而 AOP 容器负责管理切面。Spring 还提供了一系列的模块,如 Spring MVC、Spring JDBC、Spring Security 等。
这张图有点老旧了,还是 4.x 版本的,不过总体上还是没什么变化。
总体上来讲,Spring 分为以下几个部分:
-
核心技术(Core Technologies)
- IoC Container:负责管理 JavaBean 的生命周期
- Resources:资源管理
- Validation, Data binding, Type conversion:数据验证、数据绑定、类型转换
- SpEL:Spring 表达式语言
- AOP:面向切面编程
-
数据访问(Data Access)
- Transactions:事务管理
- DAO support:数据访问支持
- JDBC:JDBC 支持
- ORM:对象关系映射支持
-
Web
-
Servlet
- Web MVC
- REST
- WebSocket
-
Web Reactive
- WebFlux
- WebClient
- WebSocket
-
-
Testing
-
Integration
除此之外,Spring 拥有非常丰富的生态系统,例如:
- Spring Boot:简化 Spring 的配置和使用
- Spring Cloud:微服务架构
- Spring Security:安全框架
等等。
在实际开发中,常常会用到三层架构。
点击查看三层架构基础
三层架构是一种软件开发的设计模式,它将应用程序分为三个主要层次:
- 控制器层(Controller):负责与用户进行交互,展示数据,并接收用户输入。例如网页前端、移动应用的用户界面等。
- 业务逻辑层(Service):处理应用程序的核心功能和业务规则。例如应用程序的中间层代码,包含处理用户请求、验证输入、执行算法等逻辑。
- 持久层(Dao):负责与数据库或其他数据存储系统进行交互。例如数据访问对象,负责执行数据库查询、插入、更新和删除操作的代码。
假设我们在开发一个网上书店系统,可以将系统划分为以下三个层次:
-
控制器层:负责显示商品列表、搜索结果、商品详情页等。
public class BookControllerImpl implements BookController { private BookService bookService = new BookServiceImpl(); public void listBooks() { List<Book> books = bookService.listBooks(); for (Book book : books) { System.out.println(book); } } }
-
业务逻辑层:处理用户搜索商品、添加商品到购物车、下单等操作。
public class BookServiceImpl implements BookService { private BookDao bookDao = new BookDaoImpl(); public List<Book> listBooks() { return bookDao.listBooks(); } }
-
持久层:与数据库交互,查询商品信息、保存订单信息等。
public class BookDaoImpl implements BookDao { public List<Book> listBooks() { // 查询数据库,返回商品列表 return new ArrayList<>(); } }
在逻辑上,这三层应该是一个倒金字塔型:大量控制器层调用少量业务逻辑层,持久层最少:
这样的简单写法有几个问题:
-
资源浪费
在实际实现中,由于每个
BookControllerImpl
都使用new
创建了新的BookServiceImpl
实例、每个BookServiceImpl
都使用new
创建了新的BookDaoImpl
实例,导致了每个控制器层组件都挂了一个金字塔型的结构:这造成了极大的浪费——因为我们知道,
BookDaoImpl
很可能只需要一个实例就够用,而不是每个BookServiceImpl
都创建一个实例。 -
耦合度高
由于每个层次都直接依赖于下一层次,导致了耦合度过高。假如
BookDaoImpl
需要更换为BookDaoAnotherImpl
,那么每一个原来依赖于BookDaoImpl
的类都需要进行修改。 -
初始化和配置麻烦
由于每个层次都需要手动创建下一层次的实例,导致了初始化和配置的麻烦。例如配置一个 JDBC 需要:
dataSource = new DataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/bookstore"); dataSource.setUsername("root"); dataSource.setPassword("password"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
目前的方法下,每个
BookDaoImpl
都需要写这么长一串,而且如果数据库地址、用户名、密码等信息发生变化,每个BookDaoImpl
都需要修改。 -
测试困难
由于每个层次都直接依赖于下一层次,导致了测试困难。例如,如果要测试
BookControllerImpl
,就需要让BookServiceImpl
和BookDaoImpl
也参与测试。这样的测试方式不仅耗时,而且会导致测试结果不稳定。因为
BookServiceImpl
和BookDaoImpl
的实现可能会影响BookControllerImpl
的测试结果。
至此,IoC 的想法已经呼之欲出了:将对象的创建、配置和管理交给容器,使其与对象的使用解耦。
- 对于资源浪费的问题,IoC 容器默认使用单例模式,保证只有一个实例
- 对于耦合度高的问题,当需要更换实现类时,名称可以保持不变,只需要修改配置文件中对应的实现类即可
- 对于初始化和配置麻烦的问题,只需要在配置文件中配置一次,容器会自动读取配置文件并创建对象
- 对于测试困难的问题,只需要将测试对象注入到容器中,容器会自动创建依赖的对象
这让我想到了前端常用的状态管理库,例如 Redux 和 Pinia 等。这些库的核心思想也是如此:将对象的创建、配置和管理交给库,使其与对象的使用解耦。
Spring 的 IoC 容器是一个对象工厂,负责创建、配置和管理对象。在 IoC 容器中,对象被称为 Bean。
我们回顾一下工厂模式。
工厂模式是一种创建对象的设计模式,它提供了一个创建对象的接口,而不需要指定具体的类。
在 IoC 中,产品就是具体的类(或者说是 Bean),工厂就是 IoC 容器。客户端只需要向 IoC 容器请求某个类型的 Bean,而不需要指定具体的类。Bean 的生产和管理都交给 IoC 容器负责,客户端只需要关心 Bean 的使用。
Spring 提供了两种 IoC 容器:BeanFactory
和 ApplicationContext
。其中:
BeanFactory
是 Spring 的基础容器,如果没有特殊需求一般不用ApplicationContext
是BeanFactory
的子接口,提供了更多的功能,一般使用ApplicationContext
例如,我们有一个 Book
类:
package com.example;
public class Book {
private String title;
private String author;
public Book() {
}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
'}';
}
}
在未使用 IoC 容器时,我们需要手动创建 Book
对象:
package com.example;
public class TestBook {
@Test
public void testBook() {
Book book = new Book("Spring", "Rod Johnson");
System.out.println(book);
}
}
使用 IoC 容器后,我们则需要完成以下步骤:
-
创建一个配置文件。例如叫
Beans.xml
,配置Book
类:<?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 id="book" class="com.example.Book"> <property name="title" value="Spring"/> <property name="author" value="Rod Johnson"/> </bean> </beans>
这会将类和对应的属性都配置在 XML 文件中,形成一个 Bean 定义。
-
创建一个
ApplicationContext
对象,并从配置文件中读取book
对象:package com.example; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestBook { @Test public void testBook() { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); Book book = (Book) context.getBean("book"); System.out.println(book); } }
我们在 JVM 中讨论过类加载器的概念。
这里,我们相当于使用了当前线程的上下文类加载器,它会从
classpath
中加载Beans.xml
文件。在加载后,会完成 IoC 容器的完整初始化过程。
这样,我们就使用 IoC 容器创建了 Book
对象。
依赖注入(Dependency Injection,DI)是一种设计模式,被用于实现控制反转。
它通过将对象的依赖关系从代码中移除,转而由外部容器(如 Spring)在运行时注入。它主要有三种方式:
- 构造器注入
- setter 方法注入
- 字段注入(不推荐,因为破坏了封装性)
具体来讲,在 XML 配置中,这样使用:
构造器注入是通过构造器来注入依赖的。上面的例子中我们定义了有参构造函数 Book(String title, String author)
,可以通过 constructor-arg
元素来注入依赖:
<bean id="book" class="com.example.Book">
<constructor-arg name="title" value="Spring"/>
<constructor-arg name="author" value="Rod Johnson"/>
</bean>
Spring 会自动调用 Book
类的有参构造函数,将 title
和 author
参数传入。
setter 方法注入是通过 setter 方法来注入依赖的。例如,在上一节的例子中,我们通过 property
元素来注入 title
和 author
字段:
<bean id="book" class="com.example.Book">
<property name="title" value="Spring"/>
<property name="author" value="Rod Johnson"/>
</bean>
Spring 会自动调用 Book
类的 setTitle
和 setAuthor
方法,将 title
和 author
字段注入到 Book
对象中。
字段注入是通过字段的 setter 方法来注入依赖的。例如,在上一节的例子中,我们通过 property
元素来注入 title
和 author
vv:
<bean id="book" class="com.example.Book">
<property name="title" value="Spring"/>
<property name="author" value="Rod Johnson"/>
</bean>
Spring 会自动调用 Book
类的 setTitle
和 setAuthor
方法,将 title
和 author
属性注入到 Book
对象中。
这当中有一些细节需要注意:
-
特殊值注入
-
如果需要注入
null
,可以使用<null/>
元素:<property name="title"> <null/> </property>
-
如果字符串中包含特殊字符,可以使用
<![CDATA[]]>
来包裹:<property name="title"> <value><![CDATA[Spring & Hibernate]]></value> </property>
也可以使用 HTML 转义字符:
<property name="title"> <value>Spring & Hibernate</value> </property>
-
-
引用注入
如果需要注入另一个 Bean,可以使用
ref
属性:<bean id="author" class="com.example.Author"> <property name="name" value="Rod Johnson"/> </bean> <bean id="book" class="com.example.Book"> <property name="title" value="Spring"/> <property name="author" ref="author"/> </bean>
这也可以写成:
<bean id="book" class="com.example.Book"> <property name="title" value="Spring"/> <property name="author"> <ref bean="author"/> </property> </bean>
当然,也可以直接写在内部:
<bean id="book" class="com.example.Book"> <property name="title" value="Spring"/> <property name="author"> <bean class="com.example.Author"> <property name="name" value="Rod Johnson"/> </bean> </property> </bean>
-
集合注入
如果需要注入集合,可以使用
list
、set
、map
、props
等元素:<bean id="book" class="com.example.Book"> <property name="authors"> <list> <value>Rod Johnson</value> <value>Juergen Hoeller</value> <value>Keith Donald</value> </list> </property> </bean>
<bean id="book" class="com.example.Book"> <property name="authors"> <set> <value>Rod Johnson</value> <value>Juergen Hoeller</value> <value>Keith Donald</value> </set> </property> </bean>
<bean id="book" class="com.example.Book"> <property name="authors"> <map> <entry key="Rod Johnson" value="Spring"/> <entry key="Juergen Hoeller" value="Spring Boot"/> <entry key="Keith Donald" value="Spring Cloud"/> </map> </property> </bean>
<bean id="book" class="com.example.Book"> <property name="authors"> <props> <prop key="Rod Johnson">Spring</prop> <prop key="Juergen Hoeller">Spring Boot</prop> <prop key="Keith Donald">Spring Cloud</prop> </props> </property> </bean>
-
p 命名空间
Spring 提供了
p
命名空间,可以简化属性注入。首先需要在配置文件中引入
p
命名空间:<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
然后可以使用
p
命名空间来注入属性:<bean id="book" class="com.example.Book" p:title="Spring" p:author="Rod Johnson"/>
这等价于:
<bean id="book" class="com.example.Book"> <property name="title" value="Spring"/> <property name="author" value="Rod Johnson"/> </bean>
对于使用
ref
属性的情况,也可以使用p
命名空间:<bean id="book" class="com.example.Book" p:title="Spring" p:author-ref="author"/>
自动装配(Autowiring)是 Spring 对 DI 的一种实现方式。它可以自动识别 Bean 之间的依赖关系,从而省去了手动配置 Bean 之间的依赖关系。
Spring 提供了以下几种自动装配的方式:
-
no
默认值,不自动装配。需要手动配置 Bean 之间的依赖关系。
-
byName
根据 Bean 的名称自动装配。Spring 会自动查找与属性名相同的 Bean,并将其注入。例如:
<bean id="author" class="com.example.Author"> <property name="name" value="Rod Johnson"/> </bean> <bean id="book" class="com.example.Book"> <property name="author" ref="author"/> <property name="title" value="Spring"/> </bean>
最后一条可以被写为:
<bean id="book" class="com.example.Book" autowire="byName"> <property name="title" value="Spring"/> </bean>
可以看到,
author
与Book
的属性名相同,Spring 会自动查找author
Bean,并将其注入到Book
对象中。 -
byType
根据 Bean 的类型自动装配。Spring 会自动查找与字段类型相同的 Bean,并将其注入。
同样的,最后一条可以被写为:
<bean id="book" class="com.example.Book" autowire="byType"> <property name="title" value="Spring"/> </bean>
如果有多个 Bean 的类型相同,Spring 会抛出异常。可以使用
@Primary
注解来指定首选 Bean。 -
constructor
根据构造器参数类型自动装配。Spring 会自动查找与构造器参数类型相同的 Bean,并将其注入。
例如,
Book
类有一个构造器Book(Author author, String title)
,可以写为:<bean id="book" class="com.example.Book" autowire="constructor"> <constructor-arg value="Spring"/> </bean>
Spring 会自动查找
Author
类型的 Bean,并将其注入到Book
对象中。
以上我们都在使用 XML 文件来配置 Bean,Spring 也支持使用注解来配置 Bean。
假设我们有两个实现类 BookServiceImpl
和 BookDaoImpl
:
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public List<Book> listBooks() {
return bookDao.listBooks();
}
}
public class BookDaoImpl implements BookDao {
public List<Book> listBooks() {
// 查询数据库,返回商品列表
return new ArrayList<>();
}
}
其中,BookServiceImpl
依赖于 BookDaoImpl
。
为了实现 IoC,首先需要启用注解扫描。这有两种方法:
-
XML 文件
在 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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.example"/> </beans>
其中
base-package
属性指定了要扫描的包。 -
Java 配置类
也可以使用 Java 配置类来启用注解扫描:
以上我们依然用到了部分 XML 配置,Spring 也提供了完全基于注解的配置。例如,我们可以使用
@Configuration
注解来标记配置类:@Configuration @ComponentScan(basePackages = "com.example") public class AppConfig { }
其中,
@Configuration
注解标记了一个配置类,@ComponentScan
注解启用了注解扫描。此时,使用时不再需要加载 XML 文件,而是直接加载配置类:
public class TestBook { @Test public void testBook() { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); Book book = context.getBean(Book.class); System.out.println(book); } }
这样,我们就完全使用注解来配置 Bean 了。
然后可以使用注解,注册为 Bean:
@Service
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public List<Book> listBooks() {
return bookDao.listBooks();
}
}
@Repository
public class BookDaoImpl implements BookDao {
public List<Book> listBooks() {
// 查询数据库,返回商品列表
return new ArrayList<>();
}
}
这里有两个需要说明的地方:
@Component
注解是 Spring 的通用注解,可以用于任何类。Spring 还提供了一些更具体的注解,如@Repository
:持久层@Service
:业务逻辑层@Controller
:控制器层
- 生成的 Bean 的名称默认为类名的首字母小写,可以使用
@Component(value = "book")
来指定 Bean 的名称
当然,你也可以直接将 Bean 类写进配置类中:
@Configuration
public class AppConfig {
@Bean
public BookService bookService() {
return new BookServiceImpl();
}
@Bean
public BookDao bookDao() {
return new BookDaoImpl();
}
}
它们需要使用 @Bean
注解来标记这是一个 Bean。
注册类为 Bean 后,就可以向其中注入属性了。
如果注入的是另一个 Bean,可以使用 @Autowired
注解。例如,要想将 BookDaoImpl
注入到 BookServiceImpl
中,有这样几种方法:
-
构造器注入
@Service public class BookServiceImpl implements BookService { private BookDao bookDao; @Autowired public BookServiceImpl(BookDao bookDao) { this.bookDao = bookDao; } public List<Book> listBooks() { return bookDao.listBooks(); } }
如果写在形参上也是可以的:
@Service public class BookServiceImpl implements BookService { private BookDao bookDao; public BookServiceImpl(@Autowired BookDao bookDao) { this.bookDao = bookDao; } public List<Book> listBooks() { return bookDao.listBooks(); } }
如果只有一个构造器,
@Autowired
可以省略。- setter 方法注入
@Service public class BookServiceImpl implements BookService { private BookDao bookDao; @Autowired public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public List<Book> listBooks() { return bookDao.listBooks(); } }
-
字段注入
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; public List<Book> listBooks() { return bookDao.listBooks(); } }
这里有两个比较特殊的:
-
@Qualifier
注解注入@Autowired
默认按类型注入,如果有多个 Bean 的类型相同(例如BookDao
接口有多个实现),可以使用@Qualifier
注解来指定 Bean 的名称:@Service public class BookServiceImpl implements BookService { @Autowired @Qualifier("bookDaoImpl") private BookDao bookDao; public List<Book> listBooks() { return bookDao.listBooks(); } }
这里的
bookDaoImpl
是BookDaoImpl
类的 Bean 名称。 -
@Resource
注解注入与
@Autowired
不同,@Resource
注解是 JDK 扩展包中的,它默认按名称注入,如果找不到名称则按照类型注入。它只能被用在字段或者 setter 方法上:@Service public class BookServiceImpl implements BookService { @Resource(name = "bookDaoImpl") private BookDao bookDao; public List<Book> listBooks() { return bookDao.listBooks(); } }
这里的
bookDaoImpl
是BookDaoImpl
类的 Bean 名称。
对于非 Bean 的字段(例如基本类型、String 类型等),可以使用 @Value
注解注入:
-
@Value
注解注入@Value
注解可以用来注入基本类型、String 类型、数组、集合等。例如:@Component public class Book { @Value("Spring") private String title; @Value("Rod Johnson") private String author; @Value("${book.price}") private double price; @Value("${book.authors}") private String[] authors; @Value("#{${book.authors}}") private List<String> authorsList; }
这里有几个需要注意的地方:
- 如果需要注入的是一个基本类型,可以直接写在
@Value
注解中 - 如果需要注入的是一个 SpEL 表达式,可以使用
#{}
包裹,它会在运行时计算表达式的值,通常用于数组、集合等 - 如果需要注入的是一个外部配置文件中的值,可以使用
${}
包裹,它会在运行时读取配置文件中的值
- 如果需要注入的是一个基本类型,可以直接写在
Bean 的生命周期是指 Bean 从创建到销毁的过程。Spring 提供了多种方式来管理 Bean 的生命周期。
Spring 找到、创建、管理和销毁一个 Bean 的大致流程为:
- 扫描包,获取所有资源
- 解析资源,获取 BeanDefinition
- 管理 Bean 的生命周期
- 实例化
- 填充属性
- 初始化
@PostConstruct
InitializingBean.afterPropertiesSet()
@Bean(initMethod = "init")
- 自由使用
- 销毁
@PreDestroy
DisposableBean.destroy()
@Bean(destroyMethod = "destroy")
之所以显得如此复杂,是因为它要考虑到很多问题,比如:
- BeanFactory 和 ApplicationContext 的区别
- XML 和注解两种配置方式
- Bean 的作用域
- 多种依赖注入方式
- BeanPostProcessor
- 父子容器
其中,很多东西(比如 BeanFactory)根本没什么人用,纯纯的历史包袱,目前已经被标记为弃用了。
-
可以使用
@PostConstruct
注解来标记初始化方法:@Service public class BookServiceImpl implements BookService { @PostConstruct public void init() { System.out.println("BookServiceImpl initialized"); } public List<Book> listBooks() { return bookDao.listBooks(); } }
-
如果是定义在配置类中的 Bean,可以使用
@Bean(initMethod = "init")
来指定初始化方法:@Configuration public class AppConfig { @Bean(initMethod = "init") public BookService bookService() { return new BookServiceImpl(); } }
-
实现
InitializingBean.afterPropertiesSet()
方法也可以用来标记初始化方法:@Service public class BookServiceImpl implements BookService, InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("BookServiceImpl initialized"); } public List<Book> listBooks() { return bookDao.listBooks(); } }
-
可以使用
@PreDestroy
注解来标记销毁方法:@Service public class BookServiceImpl implements BookService { @PreDestroy public void destroy() { System.out.println("BookServiceImpl destroyed"); } public List<Book> listBooks() { return bookDao.listBooks(); } }
-
如果是定义在配置类中的 Bean,可以使用
@Bean(destroyMethod = "destroy")
来指定销毁方法:@Configuration public class AppConfig { @Bean(destroyMethod = "destroy") public BookService bookService() { return new BookServiceImpl(); } }
-
实现
DisposableBean.destroy()
方法也可以用来标记销毁方法:@Service public class BookServiceImpl implements BookService, DisposableBean { @Override public void destroy() throws Exception { System.out.println("BookServiceImpl destroyed"); } public List<Book> listBooks() { return bookDao.listBooks(); } }
BeanPostProcessor
是 Spring 提供的一个接口,可以用来在 Bean 初始化前后执行一些操作。它有两个方法:
postProcessBeforeInitialization(Object bean, String beanName)
:在 Bean 初始化前执行postProcessAfterInitialization(Object bean, String beanName)
:在 Bean 初始化后执行
例如,我们可以实现一个 BeanPostProcessor
,在 Bean 初始化前后打印日志:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Before initialization: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("After initialization: " + beanName);
return bean;
}
}
这个东西会在下一篇 AOP 中用到,现在先不管。
此外,我们还看到了处理循环依赖的方法。Spring 解决了单例模式下,setter 注入的循环依赖问题。解决的方式是通过三级缓存:
- 创建 A
- 实例化 A:调用 A 的构造方法,生成一个原始对象(未注入属性)
- 将A的工厂放入三级缓存:将生成A原始对象的
ObjectFactory
存入三级缓存(singletonFactories
) - 填充属性 B:发现 A 依赖 B,尝试从缓存获取 B,但 B 未创建,触发创建 B
- 创建 B
- 实例化 B:生成 B 的原始对象
- 将 B 的工厂放入三级缓存
- 填充属性 A:发现 B 依赖 A,尝试从缓存获取 A:
- 从三级缓存中找到 A 的
ObjectFactory
,调用其getObject()
方法获取 A 的早期引用(可能经过 AOP 代理) - 将 A 的早期引用放入二级缓存(
earlySingletonObjects
),并清理三级缓存中的 A 工厂
- 从三级缓存中找到 A 的
- 完成 B 的初始化
- 属性填充完成:B 中的 A 属性已被注入(早期引用)
- 执行初始化逻辑(如
@PostConstruct
) - 将 B 放入一级缓存:B 成为完整 Bean,存入
singletonObjects
- 完成
A
的初始化- 注入 B 的完整实例:此时 B 已在一级缓存中,A 的 B 属性被注入
- 执行初始化逻辑
- 将 A 从二级缓存移除,存入一级缓存
可以看到,解决的前提是已经实例化。这就是为什么 Spring 只解决 setter 注入的循环依赖,而不解决构造函数注入的循环依赖。
Spring 提供了多种作用域来管理 Bean 的生命周期:
- singleton:单例模式,默认值。Spring 容器只会创建一个 Bean 实例,并在整个应用程序中共享。
- prototype:原型模式。每次从容器中获取 Bean 时,都会创建一个新的 Bean 实例。
- request:请求作用域。每次 HTTP 请求都会创建一个新的 Bean 实例。仅在 Web 应用程序中有效。
- session:会话作用域。每个 HTTP 会话都会创建一个新的 Bean 实例。仅在 Web 应用程序中有效。
- globalSession:全局会话作用域。每个全局 HTTP 会话都会创建一个新的 Bean 实例。仅在 Web 应用程序中有效。
可以使用 @Scope
注解来指定 Bean 的作用域:
@Service
@Scope("prototype")
public class BookServiceImpl implements BookService {
public List<Book> listBooks() {
return bookDao.listBooks();
}
}
通常来讲,只会用到 singleton
这一种。
IoC 最核心的思想包括了以下两点:
- 控制反转:将对象的创建和管理交给 Spring 容器,而不是由其它对象来管理。Spring 容器会在特定的生命周期事件(如初始化、销毁)发生时,自动调用对象的相应方法
- 依赖查找 & 依赖注入:通过依赖查找来获取对象的引用,通过依赖注入来将对象注入到其它对象中,而不是由对象自身来获取它的依赖
方便起见,我们只看注解配置的 IoC 容器。
源码我们从 AnnotationConfigApplicationContext
开始。我们最常用的构造函数是 AnnotationConfigApplicationContext(String... basePackages)
:
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
它一共分了三步:
点击查看构造方法源码解读
this()
当然是先调用 AnnotationConfigApplicationContext()
构造函数:
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");
// 注解 Bean 读取器
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
// 注解 Bean 扫描器
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
同时还会调用父类 GenericApplicationContext
的构造函数:
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
它会创建一个 DefaultListableBeanFactory
对象:
public DefaultListableBeanFactory() {
super();
}
这个对象就是默认情况下,用来创建 Bean 的工厂类。
我们看到这玩意儿直接调用了 AbstractAutowireCapableBeanFactory
的构造函数:
public AbstractAutowireCapableBeanFactory() {
super();
// 忽略 BeanNameAware、BeanFactoryAware、BeanClassLoaderAware 等接口(下文会详细介绍)
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
// 使用 CGLIB 代理
this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
}
BeanNameAware、BeanFactoryAware 和 BeanClassLoaderAware 是 Spring 框架中的三个回调接口,允许 Bean 在初始化阶段获取与容器相关的信息。
BeanNameAware
:获取 Bean 的名称可以通过实现这个接口的
setBeanName()
方法来获取 Bean 的名称public class MyBean implements BeanNameAware { private String beanName; @Override public void setBeanName(String name) { this.beanName = name; } }
BeanFactoryAware
:获取 BeanFactory可以通过实现这个接口的
setBeanFactory()
方法来获取 BeanFactory。这可以帮助我们动态地获取 BeanFactory 中的其他 Bean 或者执行一些与容器相关的操作public class MyBean implements BeanFactoryAware { private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } public void doSomething() { MyOtherBean otherBean = beanFactory.getBean(MyOtherBean.class); // 使用 otherBean 做一些事情 } }
BeanClassLoaderAware
:获取类加载器可以通过实现这个接口的
setBeanClassLoader()
方法来获取类加载器。这在需要动态加载类或者资源时非常有用,便于实现插件化架构public class MyBean implements BeanClassLoaderAware { private ClassLoader classLoader; @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void loadClass(String className) throws ClassNotFoundException { Class<?> clazz = classLoader.loadClass(className); // 使用 clazz 做一些事情 } }这三个接口的实现类会被忽略掉,Spring 不会对它们进行依赖注入,因为它们会在初始化阶段获取与容器相关的信息,而这时容器还没有完全初始化。
这里的 super()
又调用了 AbstractBeanFactory
的构造函数,不过这个构造函数是空的。
总之,这个构造方法就是实例化了一堆工厂类。没有搞个大新闻。
点击查看扫描包源码解读
接着调用 scan(basePackages)
方法:
@Override
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
.tag("packages", () -> Arrays.toString(basePackages));
// 扫描包(下文会详细介绍)
this.scanner.scan(basePackages);
scanPackages.end();
}
继续看 ClassPathBeanDefinitionScanner
的 scan()
方法:
public int scan(String... basePackages) {
// 获取当前 BeanDefinition 的数量
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 扫描包(下文会详细介绍)
doScan(basePackages);
// 注册注解处理器
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// 返回本次注册的 BeanDefinition 的数量
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
继续看 doScan()
方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
// BeanDefinitions 使用了一个 LinkedHashSet 来存储扫描到的 BeanDefinition
// 每个元素都包含了 BeanDefinition 和 Bean 名称的二元组
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍历需要扫描的包
for (String basePackage : basePackages) {
// 获取包里所有符合条件的类以供筛选(下文会详细介绍)
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 遍历候选类
for (BeanDefinition candidate : candidates) {
// 获取作用域并绑定
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成 Bean 名称,如果指定了 Bean 名称则使用指定的名称
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 对 AbstractBeanDefinition 进行后处理
if (candidate instanceof AbstractBeanDefinition abstractBeanDefinition) {
postProcessBeanDefinition(abstractBeanDefinition, beanName);
}
// 处理类上的通用注解(如 @Lazy、@Primary、@DependsOn)
if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);
}
// 检查 Bean 是否可以注册
if (checkCandidate(beanName, candidate)) {
// 封装 BeanDefinition 和 beanName 到 BeanDefinitionHolder
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 根据作用域代理模式生成代理(即处理作用域为 request/session 的 Bean)
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 将 BeanDefinitionHolder 加入到结果集,并注册到容器中(下文会详细介绍)
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
这个函数完成了包扫描的主要流程,包括:
- 遍历所有包路径并找到候选 Bean
- 为每个候选 Bean 处理其作用域、生成 Bean 名称、后处理 BeanDefinition、处理通用注解
- 检查 Bean 是否可以注册,如果可以,则应用作用域代理模式(作用域为 request/session)并注册 BeanDefinition
- 返回注册的 BeanDefinition 集合
接下来,我们来看当中具体的几个方法:
-
findCandidateComponents()
方法:public Set<BeanDefinition> findCandidateComponents(String basePackage) { // 不存在组件索引 且 检查当前配置的包含过滤器支持索引查询 if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // 执行索引加速扫描(下文会详细介绍) return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // 执行常规路径扫描(下文会详细介绍) return scanCandidateComponents(basePackage); } }
组件索引是 Spring 提供的一种加速扫描的机制。Spring 在编译时生成一个索引文件(
META-INF/spring.components
),记录所有带有特定注解(如@Component
)的类,避免运行时扫描类路径。它可以显著加快大型应用的启动速度。-
addCandidateComponentsFromIndex()
方法会直接从索引中获取 Bean。它会:- 先找出属于
basePackage
的类 - 遍历这些类,检查是否匹配当前的
includeFilters
,如果匹配则添加到候选类中 - 对于匹配的类生成
ScannedGenericBeanDefinition
我们就不细看这个函数了。
- 先找出属于
-
scanCandidateComponents()
方法:private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // 转换包路径为资源路径,类似 classpath*:com/example/**/*.class // 其中,classpath* 表示扫描所有类路径下的资源 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 获取所有 .class 资源(包括 jar 包) Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); // 遍历资源 for (Resource resource : resources) { // 获取文件名 String filename = resource.getFilename(); // 忽略 CGLIB 生成的类(即文件名包含 $$) if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { continue; } if (traceEnabled) { logger.trace("Scanning " + resource); } try { // 通过反射获取类的元数据 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 根据过滤器包含或排除一些类 // 默认包含 @Component、@Service、@Repository、@Controller、@Configuration if (isCandidateComponent(metadataReader)) { // 将类的元数据封装为 ScannedGenericBeanDefinition // 包含了类名、作用域、是否懒加载等信息 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); // 验证类是否符合条件 // 必须是具体类、有可实例化的构造方法、如果是内部类则必须为静态类 if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } /* 省略一堆异常处理 */ return candidates; }
可以看到,这个方法会:
- 将包路径转换为资源路径
- 获取所有符合条件的资源,包括 jar 包
- 遍历资源,获取类的元数据
- 过滤出
@Component
、@Service
、@Repository
、@Controller
、@Configuration
注解的类 - 将类的元数据封装为
ScannedGenericBeanDefinition
(包含了类名、作用域、是否懒加载等信息) - 验证类是否符合条件(必须是具体类、有可实例化的构造方法、如果是内部类则必须为静态类)
- 返回符合条件的类
总体上来讲,这里就是扫描到了所有符合条件的类。
-
-
registerBeanDefinition()
方法:protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); }
继续跟踪
registerBeanDefinition()
方法:public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // 注册主名称的 BeanDefinition(下文会详细介绍) String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果存在别名,则注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
别名是 Spring 中的一个概念,用于给 Bean 起一个或多个别名。别名可以用来简化 Bean 的引用,或者提供更具语义性的名称。别名可以这样注册:
// 第一个是原始 Bean 的名称,后面都是别名 @Bean(name = {"bookService", "alias1", "alias2"}) public BookService bookService() {}
这里的 Bean 名称就是我们在上一步获取到的。
接下来,我们继续关注主名称的注册。
registerBeanDefinition()
是个接口,我们这里用的DefaultListableBeanFactory
的实现:@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); // 校验必要属性是否缺失 if (beanDefinition instanceof AbstractBeanDefinition abd) { try { abd.validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } // 处理同名 Bean 覆盖 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { if (!isBeanDefinitionOverridable(beanName)) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else { logBeanDefinitionOverriding(beanName, beanDefinition, existingDefinition); } // 覆盖 BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 如果 Bean 名称是其它 Bean 的别名 if (isAlias(beanName)) { String aliasedName = canonicalName(beanName); if (!isBeanDefinitionOverridable(aliasedName)) { if (containsBeanDefinition(aliasedName)) { throw new BeanDefinitionOverrideException( beanName, beanDefinition, getBeanDefinition(aliasedName)); } else { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition for bean '" + beanName + "' since there is already an alias for bean '" + aliasedName + "' bound."); } } else { if (logger.isInfoEnabled()) { logger.info("Removing alias '" + beanName + "' for bean '" + aliasedName + "' due to registration of bean definition for bean '" + beanName + "': [" + beanDefinition + "]"); } removeAlias(beanName); } } if (hasBeanCreationStarted()) { // 如果已经有其它的 Bean 在初始化了,需要线程安全操作 synchronized (this.beanDefinitionMap) { // 将 BeanDefinition 存入 beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); // 使用 CoW 向 List 添加新 Bean 名称 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; // 去掉手动注册的单例 Bean removeManualSingletonName(beanName); } } else { // 如果没有其它的 Bean 在初始化 // 直接操作 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } // 它被用来缓存 BeanDefinitionName,加速查找 // 重置冻结状态,使得缓存失效 this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { // 清除解析过的元数据 resetBeanDefinition(beanName); } else if (isConfigurationFrozen()) { // 清理按类型查找的缓存 clearByTypeCache(); } // 记录 @Primary 注解的 Bean 名称 if (beanDefinition.isPrimary()) { this.primaryBeanNames.add(beanName); } }
这个函数考虑了很多问题:
- 根据覆盖策略,决定是否覆盖已有的 BeanDefinition
- 如果 Bean 名称是其它 Bean 的别名,则先移除别名
- 如果已经有其它的 Bean 在初始化,则需要线程安全操作
- 清除各类缓存
- 记录 @Primary 注解的 Bean 名称
扫描包的过程其实就是将指定包下的类换成 Bean 存储到一个名为 beanDefinitions 的 LinkedHashSet 中,集合的每个元素都包含了 BeanDefinition 和 Bean 名称的二元组。然后,再将其挨个取出检查后放入 beanDefinitionMap。
最终,我们得到的是名字和 BeanDefinition 的映射关系。
点击查看刷新容器源码解读
refresh()
方法在 AbstractApplicationContext
中:
@Override
public void refresh() throws BeansException, IllegalStateException {
// 上锁,防止一个 refresh() 还没结束,另一个 refresh() 就开始了
this.startupShutdownLock.lock();
try {
// startupShutdownThread 记录当前执行线程,用于后续关闭时校验线程一致性
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 记录容器启动时间、初始化环境变量、发布 ApplicationContextEvent 早期事件(下文会详细介绍)
prepareRefresh();
// 创建或者刷新 BeanFactory(如果是 XML 配置则会在这里创建)(下文会详细介绍)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 配置类加载器、EL 解析器、注册内置 Bean 等(下文会详细介绍)
prepareBeanFactory(beanFactory);
try {
// 模板方法供子类扩展,例如 GenericWebApplicationContext 会在这里准备好 Spring Web 所需的 Bean(下文会详细介绍)
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 调用所有的 BeanFactoryPostProcessor
// 解析 @Configuration、@Bean、@ComponentScan 等注解
// 加载额外的 BeanDefinition
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化 i18n(需要名为 messageSource 的 Bean)
initMessageSource();
//初始化事件多播器,用于发布事件到监听器
initApplicationEventMulticaster();
// 模板方法,供子类初始化特殊的 Bean
onRefresh();
// 注册 ApplicationListener Bean 到事件多播器,并发布早期事件
registerListeners();
// 完成 BeanFactory 的初始化
// 初始化所有非延迟单例 Bean、触发 BeanPostProcessor、解决循环依赖等(下文会详细介绍)
finishBeanFactoryInitialization(beanFactory);
// 发布容器刷新完成事件,启动生命周期处理器
finishRefresh();
}
catch (RuntimeException | Error ex ) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
contextRefresh.end();
}
}
finally {
// 释放锁
this.startupShutdownThread = null;
this.startupShutdownLock.unlock();
}
}
这个函数我们先不急着完全理解,先看其中几个关键的函数:
-
prepareRefresh()
protected void prepareRefresh() { // 记录开始时间 this.startupDate = System.currentTimeMillis(); // 标记为活动状态,防止并发异常 this.closed.set(false); this.active.set(true); if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshing " + getDisplayName()); } } // 模板方法,供子类扩展,例如 GenericWebApplicationContext 会在这里创建 DefaultListableBeanFactory // 用于初始化和环境变量(下文会详细介绍) initPropertySources(); // 确认所有 @Require 的属性都能够被解析 getEnvironment().validateRequiredProperties(); // 备份早期事件监听器,防止多次注册 if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } this.earlyApplicationEvents = new LinkedHashSet<>(); }
这个函数是 Spring 容器刷新过程中的准备阶段。其中支持了一个
initPropertySources()
的模板方法,供子类扩展。例如,
GenericWebApplicationContext
中的实现:@Override protected void initPropertySources() { ConfigurableEnvironment env = getEnvironment(); if (env instanceof ConfigurableWebEnvironment configurableWebEnv) { configurableWebEnv.initPropertySources(this.servletContext, null); } }
这个子类覆写的方法会调用
ConfigurableWebEnvironment
的initPropertySources()
方法,初始化属性源。它会将 ServletContext 和 ServletConfig 中的属性源添加到环境变量中。 -
obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 获取 DefaultListableBeanFactory(下文会详细介绍) refreshBeanFactory(); // 返回 BeanFactory return getBeanFactory(); }
这里的两个函数都使用了接口,以适配 XML 和注解两种配置。
- 对于 XML 配置,具体的实现在
AbstractRefreshableApplicationContext
中,因为需要支持多次刷新上下文。每次调用 refresh() 时,会 销毁旧的 BeanFactory,重新创建并加载新的 BeanFactory。 - 对于注解配置,具体的实现在
GenericWebApplicationContext
中,仅需一次初始化。它的 BeanFactory 是固定的(DefaultListableBeanFactory
),仅在首次初始化时创建,后续刷新不重建
我们来看
GenericWebApplicationContext
:@Override protected final void refreshBeanFactory() throws IllegalStateException { if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException( "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); } // 使用了 DefaultListableBeanFactory(下文会详细介绍) this.beanFactory.setSerializationId(getId()); }
这里的
setSerializationId(getId())
是为了在序列化时使用当前 ApplicationContext 的 ID。public void setSerializationId(@Nullable String serializationId) { if (serializationId != null) { serializableFactories.put(serializationId, new WeakReference<>(this)); } else if (this.serializationId != null) { serializableFactories.remove(this.serializationId); } this.serializationId = serializationId; }
可以看到,
DefaultListableBeanFactory
中使用了一个WeakReference
来保存 BeanFactory 的引用,并将其放入serializableFactories
中。这里,BeanFactory 实例仅能有一个,如果已经存在,就会直接覆盖。
总体上来看,
obtainFreshBeanFactory()
方法分 XML 和注解两种配置,用不同的方式实例化了 BeanFactory。 - 对于 XML 配置,具体的实现在
-
prepareBeanFactory()
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 基础配置 // 类加载器 beanFactory.setBeanClassLoader(getClassLoader()); // SpEL 解析器,例如 #{...} beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 属性编辑器,例如 @Value("classpath:config.xml") beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // 处理 Aware 接口(下文会详细介绍) // 在 Bean 初始化前后回调 Aware 接口方法,如 setApplicationContext() beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 处理过后,Autowire 时就可以忽略它们了 beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class); // 注册可解析依赖 // 当 Bean 需要注入 BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext 时,直接返回容器实例 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 注册事件监听器 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 加载时织入(LTW)配置(下文会详细介绍) // 处理 LoadTimeWeaverAware 接口,为 Bean 注入 LoadTimeWeaver if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // 临时的 ClassLoader,确保动态生成的代理类可以被正确加载 beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 注册环境变量和属性配置、JVM 系统属性、系统环境变量、启动指标收集器的 Bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) { beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup()); } }
这个函数主要做了以下几件事:
-
设置类加载器、SpEL 解析器、属性编辑器
-
Aware 接口处理
ApplicationContextAware
接口的实现类会在初始化时调用setApplicationContext()
方法,获取当前 ApplicationContext 的引用。常用于动态获取其它 Bean、访问容器服务(如国际化、资源加载)或发布自定义事件时。public class MyBean implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void doSomething() { UserService userService = applicationContext.getBean(UserService.class); context.publishEvent(new MyCustomEvent(this)); } }
-
依赖解析
-
事件监听
事件是 Spring 中的一个重要概念,用于实现松耦合的组件之间的通信。Spring 提供了事件发布和监听的机制,可以让组件在发生特定事件时通知其它组件。
它可以用于
- 跨模块通信(如订单创建后触发库存更新)
- 异步处理(如邮件发送、日志记录)
- 系统状态监控(如应用启动、停止、异常处理)
这里是一个简单的例子:
// 定义事件 public class OrderCreateEvent extends ApplicationEvent { private final Order order; public OrderCreateEvent(Object source, Order order) { super(source); this.order = order; } public Order getOrder() { return order; } } // 发布事件 @Service public class OrderService { @Autowired private ApplicationEventPublisher eventPublisher; public void createOrder(Order order) { // 创建订单逻辑 eventPublisher.publishEvent(new OrderCreateEvent(this, order)); } } // 监听事件 @Component public class OrderEventListener { @EventListener public void handleOrderCreate(OrderCreateEvent event) { log.info("Order created: {}", event.getOrder()); } }
-
加载时织入(LTW)配置
LTW 是 Spring 提供的一种机制,用于在运行时将切面织入到目标类中。它允许开发者在不修改源代码的情况下,将切面应用到现有的类上。
这里主要是
LoadTimeWeaverAware
接口的处理。要使用
LoadTimeWeaverAware
接口,首先需要在配置类加上@EnableLoadTimeWeaving
注解。然后实现
LoadTimeWeaverAware
接口:@Component public class MyLoadTimeWeaverAware implements LoadTimeWeaverAware { private LoadTimeWeaver loadTimeWeaver; @Override public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) { this.loadTimeWeaver = loadTimeWeaver; } // 其他方法 }
然后,定义一个切面:
@Aspect @Component public class MyAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { log.info("Before method: {} ", joinPoint.getSignature()); } }
最后,运行时织入会在应用启动时自动将切面应用到目标类中。
-
环境集成
-
-
postProcessBeanFactory()
我们只看
GenericWebApplicationContext
中的实现:@Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { if (this.servletContext != null) { // 若有一个 Bean 实现 ServletContextAware,Spring 会调用其 setServletContext() 方法注入当前 ServletContext beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext)); // 忽略 ServletContextAware 接口,避免在 Autowire 时再次被注入 beanFactory.ignoreDependencyInterface(ServletContextAware.class); } // 向 BeanFactory 注册 Web 环境特有的作用域 // 包含了 request、session、application WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); // 将 Web 环境特有的对象注册为单例 Bean,供其他组件依赖注入 // 包括了 ServletContext、ServletConfig、ContextParameters、ContextAttributes WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext); }
这个函数整体上就是为 Spring Web 完成了一些配置。
-
finishBeanFactoryInitialization()
我们跳过了其它一些不太重要的函数,直接来到最后一步。这个函数会完成 BeanFactory 的初始化:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // 初始化引导执行器 if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) && beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) { beanFactory.setBootstrapExecutor( beanFactory.getBean(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)); } // 初始化转换服务(即 Bean 的类型为 ConversionService,用于 Bean 的类型转换) if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // 注册默认的嵌入式值解析器,用于解析 ${...} 占位符 if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // 初始化 BeanFactoryInitializer 的 Bean // BeanFactoryInitializer 是一个回调接口,用于在 BeanFactory 完成初始化后执行一些自定义的初始化逻辑 String[] initializerNames = beanFactory.getBeanNamesForType(BeanFactoryInitializer.class, false, false); for (String initializerName : initializerNames) { beanFactory.getBean(initializerName, BeanFactoryInitializer.class).initialize(beanFactory); } // 初始化 LoadTimeWeaverAware 的 Bean // LoadTimeWeaverAware Bean 需要在类加载时织入切面,必须尽早初始化以注册字节码转换器 String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { try { beanFactory.getBean(weaverAwareName, LoadTimeWeaverAware.class); } catch (BeanNotOfRequiredTypeException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to initialize LoadTimeWeaverAware bean '" + weaverAwareName + "' due to unexpected type mismatch: " + ex.getMessage()); } } } // 清理 LTW 相关的临时 ClassLoader beanFactory.setTempClassLoader(null); // 已注册的 Bean 不再允许被修改或后处理,提升性能 beanFactory.freezeConfiguration(); // 初始化非延迟单例 Bean(下文会详细介绍) beanFactory.preInstantiateSingletons(); }
这个函数根据优先级,依次初始化了:
- 引导执行器和转换服务
- LTW 相关的 Bean
- 普通的单例 Bean
总体来讲,就是在真正初始化 Bean 前将一些可能依赖的 Bean 初始化好。
跟踪最后的初始化函数:
@Override public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // 先拷贝一份 Bean 名称,因为后面回往其中添加新的 Bean 名称,破坏遍历的稳定性 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) { // 合并父子类的 BeanDefinition RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 仅处理非抽象、非延迟的单例 Bean if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // FactoryBean 需要特殊处理进行提前实例化(下文会详细介绍) if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) { getBean(beanName); } } else { // 实例化普通 Bean(下文会详细介绍) getBean(beanName); } } } // 后处理 for (String beanName : beanNames) { // 获取实例单例 Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) { StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize") .tag("beanName", beanName); // 如果实现了 SmartInitializingSingleton 接口,则调用 afterSingletonsInstantiated()(下文会详细介绍) smartSingleton.afterSingletonsInstantiated(); smartInitialize.end(); } } }
这里除了实例化普通 Bean,还特殊处理了 SmartFactoryBean 和 SmartInitializingSingleton。
-
SmartFactoryBean
是FactoryBean
的子接口,提供了isEagerInit()
方法,用于判断是否需要提前实例化。public class mySmartFactoryBean implements SmartFactoryBean<MyBean> { @Override public boolean isEagerInit() { return true; } @Override public MyBean getObject() { return new MyBean(); } }
-
SmartInitializingSingleton
是一个回调接口,用于在所有单例 Bean 实例化完成后执行一些自定义的初始化逻辑。@Component public class MySmartInitializingSingleton implements SmartInitializingSingleton { @Override public void afterSingletonsInstantiated() { log.info("All singletons are instantiated"); // 自定义初始化逻辑 } }
接下来,我们来看一下
getBean()
方法。这个方法巨长,但逻辑并不复杂:protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 处理名称,主要是去除 FactoryBean 的前缀 & String beanName = transformedBeanName(name); Object beanInstance; // 检查是否已经实例化过,如果已经实例化过,直接返回 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 防止原型 Bean 循环依赖 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 如果 Bean 有父类且没有实例化过 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory abf) { return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } // 标记为已创建,防止并发下多次实例化 if (!typeCheckOnly) { markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try { if (requiredType != null) { beanCreation.tag("beanType", requiredType::toString); } // 合并父子类的 BeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // 获取依赖的 Bean,确保依赖先于当前 Bean 被实例化 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // 检查循环依赖,检测到就抛出异常 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // 注册依赖关系 registerDependentBean(dep, beanName); try { // 递归初始化依赖 Bean getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } catch (BeanCreationException ex) { if (requiredType != null) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Failed to initialize dependency '" + ex.getBeanName() + "' of " + requiredType.getSimpleName() + " bean '" + beanName + "': " + ex.getMessage(), ex); } throw ex; } } } // 按照不同的作用域实例化 Bean // 单例模式 if (mbd.isSingleton()) { // 三级缓存解决循环依赖(下文会详细介绍) sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // 原型模式,没有缓存,直接实例化 else if (mbd.isPrototype()) { Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } // 自定义作用域,包括了 request、session 等 else { String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean '" + beanName + "'"); } Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); // BeanFactory 实例,获取其生产的 Bean 实例 beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) { beanCreation.tag("exception", ex.getClass().toString()); beanCreation.tag("message", String.valueOf(ex.getMessage())); cleanupAfterBeanCreationFailure(beanName); throw ex; } finally { beanCreation.end(); if (!isCacheBeanMetadata()) { clearMergedBeanDefinition(beanName); } } } // 返回适配后的 Bean 实例,确保类型正确,可能会进行类型转换 return adaptBeanInstance(name, beanInstance, requiredType); }
总之,这个函数就是先去做了循环依赖检查,然后分单例、原型、自定义三种作用域,分别实例化 Bean。
当然,如果父类还没实例化完,它会先实例化父类。
有必要讲一下这里是如何实现三级缓存的。三级缓存的核心思想是提前暴露未初始化的 Bean 引用,允许循环依赖。
// 三级缓存,存放 Bean 的 ObjectFactory private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 二级缓存,存放早期暴露的 Bean,未完成属性填充和初始化 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 一级缓存,存放已完成属性填充和初始化的 Bean private final Map<String, Object> singletons = new HashMap<>(256);
为了摸清三级缓存的使用,我们来看
createBean()
方法:@Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isTraceEnabled()) { logger.trace("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // 根据类名解析出 Bean 的 Class Class<?> resolvedClass = resolveBeanClass(mbd, beanName); // 如果这个类是动态解析的(例如 @Configuration 中的 @Bean),则需要克隆出新的 RootBeanDefinition if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); try { // 确认是否有 @Override,如果没有则不需要 CGLIB 增强 mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } } // 调用所有 InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation 方法 // 用于 AOP 代理和自定义代理对象等 try { Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } // 真正实例化 Bean(下文会详细介绍) try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
这个函数主要是把动态类和 PostProcessor(如代理类)单独拎出来处理了下。
我们继续看
doCreateBean()
方法:protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 创建 Bean 的原始实例 // 可能通过构造函数、工厂方法、CGLIB 等方式创建 BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 创建 Bean 实例(下文会详细介绍) instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); // 获取 Bean 的类型 Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // 执行后处理器 // @Autowired、@Value 等注解注入元数据 // @PostConstruct、@PreDestroy 等生命周期回调 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.markAsPostProcessed(); } } // 提前暴露 Bean 引用 // 将 Bean 的 ObjectFactory 存入 singletonFactories,允许其他 Bean 在依赖注入时获取早期引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 初始化 Bean 实例 Object exposedObject = bean; try { // 填充属性(下文会详细介绍) populateBean(beanName, mbd, instanceWrapper); // 执行初始化回调 // 这个函数会执行 @PostConstruct 注解的方法,但竟然标注了 @Deprecated! exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) { throw bce; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex); } } // 处理早期冲突,避免在 BeanPostProcessor 之外过早访问未初始化的 Bean if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = CollectionUtils.newLinkedHashSet(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } // 注册实现了 DisposableBean 接口的 Bean try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
这个函数依次完成了:
- 实例化 Bean
- 提前暴露 Bean 引用
- 填充属性
- 执行初始化回调和代理生成
- 处理早期冲突
- 销毁注册
下面,我们来看看填充属性和初始化回调的实现:
-
createBeanInstance()
方法该方法会根据 Bean 的定义和参数创建一个新的 Bean 实例。它会考虑到构造函数、工厂方法、CGLIB 代理等多种情况。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // 根据 Bean 定义中的类名解析出具体的 Class Class<?> beanClass = resolveBeanClass(mbd, beanName); // 必须是 public 的类 if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } // Bean 定义中配置了自定义的 Supplier,直接使用它创建实例 if (args == null) { Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName, mbd); } } // 如果 Bean 定义中配置了工厂方法,则使用它创建实例 if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // 复用缓存的构造函数解析结果 // 避免重复解析构造函数,提升性能 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 确定候选构造函数 ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // 构造函数自动装配 return instantiateBean(beanName, mbd); }
这个函数确定了拿什么实例化 Bean,它会依次尝试:
- 自定义的 Supplier
- 工厂方法
- @Autowired 构造函数
- 默认构造函数
-
自定义的 Supplier 主要是指
@Bean
注解中的Supplier
,它会在 Bean 定义中被解析出来。例如:
@Configuration public class MyConfig { @Bean public Supplier<UserService> userServiceSupplier() { return () -> { UserService userService = new UserService(); userService.init(); return userService; }; } @Bean public UserService userService(Supplier<UserService> userServiceSupplier) { return userServiceSupplier.get(); } } @FunctionalInterface public interface Supplier<T> { T get(); }
这个函数使得在实例化
UserService
时,可以使用userServiceSupplier
中定义的逻辑。 -
工厂方法允许开发者将对象创建逻辑封装在工厂类中。它和
supplier
差不多,不再赘述。
方便起见,我们看最后一个
instantiateBean()
方法:protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { // 实例化有两种策略:反射调用和 CGLIB Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); // 包装并初始化 Bean BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex); } }
继续跟踪
instantiate()
方法:@Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; // 如果不存在方法覆写,那就使用 Java 反射进行实例化 // 获取构造函数 synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { // 获取默认无参构造函数 constructorToUse = clazz.getDeclaredConstructor(); bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else { // 否则使用 CGLIB return instantiateWithMethodInjection(bd, beanName, owner); } }
没什么好说的,就是用反射找到构造函数并调用。
往下挖了这么多层,不要忘了我们最开始看的是
createBeanInstance()
方法。我们看到,它分构造函数、工厂方法、CGLIB 代理等多种情况,分别对应不同的实例化策略。最终,会返回一个未初始化的实例。
接下来,我们继续看它是如何填充属性的:
-
populateBean()
方法:protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // 确认已经成功实例化 Bean if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { return; } } // Record 类型不可变,不允许修改属性值 if (bw.getWrappedClass().isRecord()) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to a record"); } else { return; } } // 自定义依赖注入 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } // 处理自动装配 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // 按名称 Autowire if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // 按类型 Autowire if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } // 处理属性值 if (hasInstantiationAwareBeanPostProcessors()) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 修改属性值 PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } pvs = pvsToUse; } } // 依赖检查 boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); if (needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); checkDependencies(beanName, mbd, filteredPds, pvs); } // 将属性值应用到 Bean 实例 if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } }
它做了这么几件事:
- 校验是否可以填充属性
- 处理自动装配
- 后处理扩展
- 完成注入
后处理扩展就是调用
InstantiationAwareBeanPostProcessor
接口的postProcessProperties()
方法,允许开发者在属性填充前后进行自定义处理。例如:
@Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { for (PropertyValue pv : pvs.getPropertyValues()) { if ("name".equals(pv.getName())) { pvs.addPropertyValue("name", "newName"); } } return pvs; } }
好!啰啰嗦嗦地说了这么多,总算看完了 Spring 刷新容器的过程。
这个过程中,Spring 主要就是将 BeanDefinition 创建了实例并注入了属性。
我们最后来总结一些 IoC 的流程:
-
遍历所有包路径并找到候选 Bean
-
直接从索引中获取
- 先找出属于 basePackage 的类
- 遍历这些类,检查是否匹配当前的 includeFilters,如果匹配则添加到候选类中
- 对于匹配的类生成 ScannedGenericBeanDefinition
-
通过扫描包目录获取
- 将包路径转换为资源路径
- 获取所有符合条件的资源,包括 jar 包
- 遍历资源,获取类的元数据
- 过滤出
@Component
、@Service
、@Repository
、@Controller
、@Configuration
注解的类 - 将类的元数据封装为 ScannedGenericBeanDefinition(包含了类名、作用域、是否懒加载等信息)
- 验证类是否符合条件(必须是具体类、有可实例化的构造方法、如果是内部类则必须为静态类)
- 返回符合条件的类
-
-
为每个候选 Bean 处理其作用域、生成 Bean 名称、后处理 BeanDefinition、处理通用注解
-
检查 Bean 是否可以注册,如果可以,则应用作用域代理模式(作用域为
request
/session
)并注册 BeanDefinition-
注册主要名称
- 根据覆盖策略,决定是否覆盖已有的 BeanDefinition
- 如果 Bean 名称是其它 Bean 的别名,则先移除别名
- 如果已经有其它的 Bean 在初始化,则需要线程安全操作
- 清除各类缓存
- 记录
@Primary
注解的 Bean 名称
-
注册别名
-
-
返回注册的 BeanDefinition 集合
-
记录容器启动时间并初始化
- 初始化属性源
- 确认
@Require
属性都已满足 - 备份早期事件监听器
-
创建或刷新 BeanFactory
-
XML 配置
销毁旧的 BeanFactory,重新创建并加载新的 BeanFactory
-
注解配置
仅在首次初始化时创建 BeanFactory,后续刷新不重建
-
-
配置类加载器、EL 解析器、注册内置 Bean 等
- 设置类加载器、SpEL 解析器、属性编辑器
- 处理
ApplicationContextAware
接口 - 依赖解析
- 事件监听
- 加载时织入(LTW)配置,处理
LoadTimeWeaverAware
接口 - 环境集成
-
调用所有的 BeanFactoryPostProcessor
为 Spring Web 完成基础配置
-
初始化 i18n
-
初始化事件多播器
-
初始化特殊的 Bean
-
初始化所有非延迟单例 Bean、触发 BeanPostProcessor、解决循环依赖
-
初始化引导执行器和转换服务
-
初始化 LTW 相关的 Bean
-
初始化普通的单例 Bean
-
特殊处理
SmartFactoryBean
-
实例化普通 Bean
执行循环依赖检查,如果父类还没实例化完,它会先实例化父类。
-
单例实例化
-
处理 InstantiationAwareBeanPostProcessor
-
实例化 Bean
- 使用自定义的 Supplier
- 使用工厂方法
- @Autowired 构造函数
- 默认构造函数
-
提前暴露 Bean 引用
-
填充属性
- 校验是否可以填充属性
- 处理自动装配
- 后处理扩展(InstantiationAwareBeanPostProcessor)
- 完成注入
-
执行初始化回调
-
处理早期冲突
-
注册实现了 DisposableBean 接口的 Bean
-
-
原型实例化
-
自定义作用域实例化
-
-
特殊处理
SmartInitializingSingleton
-
-
-
发布容器刷新完成事件,启动生命周期处理器
根据以上讨论,我们可以总结出 Bean 的生命周期:
-
BeanDefinition 注册(不属于 Bean 生命周期)
通过
@ComponentScan
扫描到的 Bean 会被注册到容器中执行
ApplicationContextAware
和LoadTimeWeaverAware
-
实例化
根据包扫描的结果,通过构造函数或工厂方法创建 Bean 实例
为解决循环依赖,Spring 可能提前暴露未初始化的 Bean
-
依赖注入
根据
@Value
、@Autowired
、@Resource
等注解注入属性值和依赖如果存在循环依赖,从三级缓存获取提前暴露的 Bean 引用
-
Aware 接口回调
执行 Aware 接口的回调方法
按顺序执行
BeanNameAware
$$\to$$ BeanClassLoaderAware
$$\to$$ BeanFactoryAware
-
BeanPostProcess 前置处理
执行
BeanPostProcessor
的postProcessBeforeInitialization
方法 -
初始化方法
依次处理
-
@PostConstruct
注解的方法 -
InitializingBean
接口的afterPropertiesSet()
方法 - 自定义的初始化方法
-
-
BeanPostProcess 后置处理
执行
BeanPostProcessor
的postProcessAfterInitialization
方法 -
Bean 就绪
所有 Bean 都放入一级缓存,以供使用
-
销毁
依次处理
-
@PreDestroy
注解的方法 -
DisposableBean
接口的destroy()
方法 - 自定义的销毁方法
-
上文我们已经介绍了一些扩展点,并给出了示例代码。这里我们再总结一下:
-
BeanFactoryPostProcessor
在 BeanDefinition 加载完成后,Bean 实例化之前执行。可以修改 BeanDefinition 的属性。
解析
${}
占位符、处理@Configuration
注解等都是通过这个接口实现的。例如,修改 Bean 的作用域:
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { BeanDefinition bd = beanFactory.getBeanDefinition("myBean"); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); } }
-
BeanDefinitionRegistryPostProcessor
在 BeanDefinition 加载完成后,Bean 实例化之前执行。可以动态注册新的 BeanDefinition。
组件扫描、
@Configuration
注解等都是通过这个接口实现的。例如,动态注册一个 Bean:
@Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { registry.registerBeanDefinition("customBean", new RootBeanDefinition(CustomBean.class)); } }
-
BeanPostProcessor
在 Bean 实例化后,属性注入前执行。可以修改 Bean 的属性。
@Autowired
、@Resource
、@PostConstruct
等都是通过这个接口实现的。例如,修改 Bean 的属性:
@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (bean instanceof MyBean) { ((MyBean) bean).setName("newName"); } return bean; } }
-
FactoryBean
通过实现
FactoryBean
接口,可以自定义 Bean 的创建逻辑。例如,创建一个单例的
UserService
:@Component public class UserServiceFactoryBean implements FactoryBean<UserService> { @Override public UserService getObject() { return new UserService(); } @Override public Class<?> getObjectType() { return UserService.class; } @Override public boolean isSingleton() { return true; } }
-
Aware
通过实现
Aware
接口,可以获取 Spring 容器的上下文信息。其包括了
ApplicationContextAware
、BeanFactoryAware
、EnvironmentAware
等接口。例如,获取
ApplicationContext
:@Component public class MyBean implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext applicationContext) { logger.info("ApplicationContext: " + applicationContext); } }
-
自定义作用域
-
@PostConstruct
和@PreDestroy
通过这两个注解,可以在 Bean 初始化和销毁时执行自定义逻辑。
例如:
@Component public class MyBean { @PostConstruct public void init() { logger.info("MyBean initialized"); } @PreDestroy public void destroy() { logger.info("MyBean destroyed"); } }
-
ApplicationListener
通过实现
ApplicationListener
接口,可以监听 Spring 容器中的事件。例如,监听容器刷新事件:
@Component public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { logger.info("Application context refreshed: " + event.getApplicationContext()); } }
-
ImportBeanDefinitionRegistrar
和@Import
通过实现
ImportBeanDefinitionRegistrar
接口,可以动态注册 BeanDefinition。MyBatis 的
@MapperScan
扫描就是通过这个接口实现的。例如:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registry.registerBeanDefinition("customBean", new RootBeanDefinition(CustomBean.class)); } }
然后在配置类中使用
@Import
注解:@Configuration @Import(MyImportBeanDefinitionRegistrar.class) public class MyConfig { // ... }
-
InstantiationAwareBeanPostProcessor
通过实现
InstantiationAwareBeanPostProcessor
接口,可以在 Bean 实例化前后执行自定义逻辑。AOP 代理就是通过这个接口实现的。
例如,修改 Bean 的属性:
@Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) { if (bean instanceof MyBean) { ((MyBean) bean).setName("newName"); } return true; } }
-
@Conditional
与Condition
通过实现
Condition
接口,可以根据条件动态注册 BeanDefinition。Spring Boot 的自动配置就是通过这个接口实现的。
例如:
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment().getProperty("my.property").equals("true"); } }
然后在配置类中使用
@Conditional
注解:@Configuration @Conditional(MyCondition.class) public class MyConfig { // ... }
-
PropertyEditor
与ConversionService
通过实现
PropertyEditor
接口,可以自定义属性的转换逻辑。例如:
public class MyPropertyEditor extends PropertyEditorSupport { @Override public void setAsText(String text) { setValue(new MyBean(text)); } }
然后在配置类中注册:
@Configuration public class MyConfig { @Bean public MyPropertyEditor myPropertyEditor() { return new MyPropertyEditor(); } }
-
ApplicationContextInitializer
通过实现
ApplicationContextInitializer
接口,可以在 Spring 容器刷新之前执行自定义逻辑。例如:
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.getEnvironment().setActiveProfiles("dev"); } }
然后在
META-INF/spring.factories
中注册:org.springframework.context.ApplicationContextInitializer=\ com.example.MyApplicationContextInitializer
-
BeanNameGenerator
通过实现
BeanNameGenerator
接口,可以自定义 Bean 的名称。例如:
public class MyBeanNameGenerator implements BeanNameGenerator { @Override public String generateBeanName(AnnotatedBeanDefinition definition, BeanDefinitionRegistry registry) { return "myBean"; } }
然后在配置类中使用
@ComponentScan
注解:@Configuration @ComponentScan(nameGenerator = MyBeanNameGenerator.class) public class MyConfig { // ... }