第01章:开篇介绍,我要带你撸 Spring 啦! | 小傅哥 bugstack 虫洞栈
我的仓库:https://github.com/CMD137/mini-spring
Step 1:创建简单的Bean容器:
spring 的特性IOC的核心就是Bean容器,Spring 容器负责创建、配置和管理 Bean 对象。
在 Spring Bean 容器的场景下,我们需要一种可以用于存放和名称索引式的数据结构,所以选择HashMap(ConcurrentHashMap)。
本节实现Bean 的定义、注册、获取三个基本步骤。
public class BeanDefinition {
private Object bean;
public BeanDefinition(Object bean){
this.bean = bean;
}
public Object getBean(){
return bean;
}
}
public interface BeanFactory {
public Object getBean(String name);
public void registerBeanDefinition(String name, BeanDefinition beanDefinition);
}
public class DefaultBeanFactory implements BeanFactory{
private ConcurrentMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
@Override
public Object getBean(String name) {
Object bean = beanDefinitionMap.get(name).getBean();
if (bean == null){
throw new RuntimeException("bean not found");
}
return bean;
}
@Override
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
if (beanDefinitionMap.containsKey(name)){
throw new RuntimeException("bean already exists");
}
beanDefinitionMap.put(name,beanDefinition);
}
}
此节的BeanDefinition 使用Object管理,所以在注册时先得实例化。要把实例化也交给spring容器,就要用class而非Object。下节进行改造。
Step 2:实现Bean 的定义、注册、获取:
目标:
- 把 Bean 的创建交给容器,而不是我们在调用时候传递一个实例化好的 Bean 对象,
- 使用单例模式、创建单例对象,在对象的二次获取时是可以从内存(实例池、实例表)中获取对象的。
- 使用模板模式,为将来扩容其他功能做好扩展性高的架构。
Spring Bean 容器类关系:
具体实现:
0.异常类:
工具,没什么好说的。
public class BeansException extends RuntimeException{
public BeansException(String msg) {
super(msg);
}
public BeansException(String msg, Throwable cause) {
super(msg, cause);
}
}
1.BeanDefinition 定义:
BeanDefinition
是 IoC 容器的核心元信息载体,它描述了一个 bean 的定义,包括它的 class 类型、作用域、构造参数、属性等。
在当前阶段的简化版本中,它只包含了 beanClass
字段,用来保存 bean 的 Class
对象,代替了Step1的Object,这样就可以把 Bean 的实例化操作放到容器中处理了。
public class BeanDefinition {
private Class beanClass;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
}
public Class getBeanClass() {
return beanClass;
}
}
2.单例注册接口定义和实现:
定义了一个获取单例对象的接口:
public interface SingletonBeanRegistry {
Object getSingleton(String beanName);
}
在 DefaultSingletonBeanRegistry 中主要实现 getSingleton 方法,同时实现了一个受保护的 addSingleton 方法。维护了单例对象池。实现了最基础的单例对象注册与获取机制。
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
//单例对象池
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
@Override
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}
protected void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}
}
3.Bean创建工厂的抽象类 AbstractBeanFactory:
BeanFactory 的抽象实现,封装了“获取 Bean”的整体流程:
* 先从单例池获取;
* 如果没有,就拿到定义、创建 Bean、注册到单例池。
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
@Override
public Object getBean(String beanName) {
Object bean = getSingleton(beanName);
//如果bean实例已经存在,直接返回;
if (bean != null) {
return bean;
}
//不存在,才创建并返回:
BeanDefinition beanDefinition = getBeanDefinition(beanName);
return createBean(beanName,beanDefinition);
}
protected abstract BeanDefinition getBeanDefinition(String beanName);
protected abstract Object createBean(String beanName,BeanDefinition beanDefinition);
}
4.实例化Bean类 AbstractAutowireCapableBeanFactory:
负责真正地“new 一个 Bean”,然后加入单例池。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) {
Object bean = null;
try {
bean = beanDefinition.getBeanClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new BeansException("Instantiation of bean failed",e);
}
addSingleton(beanName,bean);
return bean;
}
}
在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作 newInstance
,其实这块会埋下一个坑,有构造函数入参的对象怎么处理?
5.注册BeanDefinition的接口 BeanDefinitionRegistry:
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}
为什么这里有这个接口?既然是注册,为什么不把注册表写进来,变成一个抽象类,让最终类继承?
答:““定义”属于 Registry,“创建、管理”属于 Factory。”另外,从语言角度来说,java不支持多继承,但可以同时 继承+实现接口。
6.核心类实现(DefaultListableBeanFactory):
- 既是 Bean 工厂的具体实现类,又是 BeanDefinition 注册表的实现。
- 它持有并管理所有的 BeanDefinition(Bean 的元信息),保证 Bean 注册的唯一性。
- 通过实现
BeanDefinitionRegistry
接口,实现了向容器中注册 Bean 定义的能力。 - 继承了
AbstractAutowireCapableBeanFactory
,获得了根据 BeanDefinition 创建 Bean 实例的能力。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry{
//bean注册表
private Map<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();
@Override
protected BeanDefinition getBeanDefinition(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null){
throw new BeansException("No bean named '" + beanName + "' is defined");
}
return beanDefinition;
}
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
if (beanDefinitionMap.containsKey(beanName)){
throw new BeansException(beanName + "already exists");
}
beanDefinitionMap.put(beanName,beanDefinition);
}
}
核心的两个map:
对象名 | beanDefinitionMap Bean注册表 | singletonObjects Bean单例池 |
---|---|---|
存储内容 | Bean 的定义信息(BeanDefinition) | 已创建的单例 Bean 实例(Object) |
所在类 | DefaultListableBeanFactory | DefaultSingletonBeanRegistry |
作用 | 管理 Bean 注册信息,告诉容器如何创建 Bean | 缓存单例实例,保证 Bean 唯一 |
访问时机 | 创建 Bean 之前获取定义 | 取用已创建的 Bean 实例 |
测试:
@org.junit.jupiter.api.Test
public void test_BeanFactory(){
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2.注册 bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 3.第一次获取 bean:新创建的对象。
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
// 4.第二次获取 bean from Singleton,从实例map表里取得同一个对象。
UserService userService_singleton = (UserService) beanFactory.getBean("userService");
userService_singleton.queryUserInfo();
// 5.观察二者的hashCode,验证是否是一个实例:
System.out.println("userService.hashCode:"+userService.hashCode());
System.out.println("userService_singleton.hashCode:"+userService_singleton.hashCode());
}
结果:
查询用户信息
查询用户信息
userService.hashCode:945591847
userService_singleton.hashCode:945591847
Step 3:基于Cglib实现含参构造函数的类的实例化策略:
目标:
添加含参构造函数的实例化方式。
两个问题:
- 从哪合理的把构造函数的入参信息传递到实例化操作里?
参考 Spring Bean 容器源码的实现方式,在 BeanFactory 中添加 Object getBean(String name, Object... args)
接口,这样就可以在获取 Bean 时把构造函数的入参信息传递进去了。
- 怎么去实例化含有构造函数的对象?
两个方法:
- 基于 Java 本身自带的方法
DeclaredConstructor
- 使用 Cglib 来动态创建 Bean 对象。
真实Spring 是怎么做的?
Spring 有两种主要的策略类:
SimpleInstantiationStrategy
:用 Java 反射调用构造函数。CglibSubclassingInstantiationStrategy
:当需要创建代理时用。
Spring 中 BeanDefinition
会包含构造参数信息,最终通过 ConstructorResolver
进行自动匹配。
Spring Bean 容器类关系:
具体实现:
1. 新增 getBean 接口
public interface BeanFactory {
Object getBean(String beanName) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
}
2.优化AbstractBeanFactory:
- 引入支持构造参数的重载
getBean
方法和统一的doGetBean
处理流程,实现了更灵活的有参实例化能力 - 增加了泛型支持以提升类型安全,并且使得获取是不再手动类型转换
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, args);
}
protected <T> T doGetBean(final String name, final Object[] args) {
Object bean = getSingleton(name);
if (bean != null) {
return (T) bean;
}
BeanDefinition beanDefinition = getBeanDefinition(name);
return (T) createBean(name, beanDefinition, args);
}
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;
}
3. 定义实例化策略接口
public interface InstantiationStrategy {
Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;
}
补充:Constructor 是什么?
在 Java 中,Constructor
是反射 API 提供的类,代表某个类的某个构造函数
可以用它做:
- 获取构造函数元信息(参数类型、注解等)
- 调用构造函数创建对象
参数 | 解释 |
---|---|
Constructor ctor | 指定 用哪个构造函数 创建 Bean 实例 |
Object[] args | 构造函数的 入参值 |
4.JDK实例化方法:
SimpleInstantiationStrategy
使用 Java 原生反射,根据是否传入构造器(ctor)选择合适方式 new 出对象,是最基础的实例化策略。spring默认策略。
public class SimpleInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Class clazz = beanDefinition.getBeanClass();
try {
if (null != ctor) {
return ctor.newInstance(args);
} else {
return clazz.getDeclaredConstructor().newInstance();
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
}
}
}
5. Cglib 实例化方法:
记得导包:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
//CGLIB 的核心类,用来生成子类对象的工具。
Enhancer enhancer = new Enhancer();
//设置“目标类”为被代理的父类(也就是你的 Bean 原始类),CGLIB 是通过 继承(子类)方式代理的
enhancer.setSuperclass(beanDefinition.getBeanClass());
//设置回调对象(Callback),即当代理类的方法被调用时,如何进行“拦截”处理。
enhancer.setCallback(new NoOp() {
//NoOp 是 CGLIB 内置的一个回调实现,表示“不做任何增强”。这里只是在演示。可替换为动态代理、懒加载等...
@Override
public int hashCode() {
return super.hashCode();
}
});
/*
判断是否有构造器:
如果没有,调用默认构造方法 enhancer.create()
如果有参数构造方法,则通过 enhancer.create(ctor.getParameterTypes(), args) 创建对象
*/
if (null == ctor) return enhancer.create();
return enhancer.create(ctor.getParameterTypes(), args);
}
}
在需要“生成子类”来实现更高级行为时(如:方法注入、动态代理、AOP),就使用这个策略。
6优化为创建bean时使用Cglib 实例化方法:
AbstractAutowireCapableBeanFactory引入了“实例化策略”模式,将具体创建对象的过程委托给策略类
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
//定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
Constructor constructorToUse = null;
Class beanClass = beanDefinition.getBeanClass();
//通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合
Constructor[] declaredConstructors = beanClass.getDeclaredConstructors();
//循环比对匹配构造函数与args,此处简化为数量对比,实际还需比对入参类型
for (Constructor ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}
public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}
public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}
}
测试:
改造测试bean:
public class UserService {
private String name;
public UserService(String name) {
this.name = name;
}
public void queryUserInfo() {
System.out.println("查询用户信息:" + name);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("");
sb.append("").append(name);
return sb.toString();
}
}
测试1:
@org.junit.jupiter.api.Test
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 3.获取bean
UserService userService = (UserService) beanFactory.getBean("userService", "CMD137");
userService.queryUserInfo();
}
结果:
查询用户信息:CMD137
Step 4:注入属性和依赖对象:
目标:
- 通过反射机制将 Bean 对象的字段进行赋值(属性注入)。
- 识别字段上的
@Value
和@Autowired
注解,执行对应注入逻辑。@Value
: 注入基本类型值(如字符串、数字)。@Autowired
: 注入另一个 Bean(依赖注入)。
设计:
- Spring 的依赖注入(DI)流程是:先创建对象(实例化),再给它注入属性(依赖)。在
createBean()
方法中,先实例化 Bean,再执行applyPropertyValues()
方法来填充属性。applyPropertyValues()
就是执行依赖注入的关键步骤。 - 要给 Bean 注入哪些属性,是在 Bean 定义(
BeanDefinition
)中预先描述好的。所以必须给BeanDefinition
类加一个属性:PropertyValues
,用于记录所有要注入的字段及其值。PropertyValues
类中包含多个PropertyValue
对象,每个对象代表一个字段名和值(或者是引用 Bean)。 - 有些属性要注入的是另一个 Bean(对象),而不是简单的字符串或整数。比如:
@Autowired private UserService userService;
在配置中(或注解解析中)这类属性会以"userService"
这样的 Bean 名称出现,而不是具体的对象。所以引入了一个BeanReference
类:它不是直接保存对象,而是保存要注入的 Bean 的名称。当注入时,根据这个名称再去容器里找对应的 Bean —— 如果没有,就递归创建它。
Spring Bean 容器类关系:
具体实现:
1.定义属性类及属性集合类:
public class PropertyValue {
private final String name;
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<>();
public void addPropertyValue(PropertyValue pv) {
this.propertyValueList.add(pv);
}
public PropertyValue[] getPropertyValues() {
return this.propertyValueList.toArray(new PropertyValue[0]);
}
public PropertyValue getPropertyValue(String propertyName) {
for (PropertyValue pv : this.propertyValueList) {
if (pv.getName().equals(propertyName)) {
return pv;
}
}
return null;
}
}
2.添加属性类到Bean定义:
public class BeanDefinition {
private Class beanClass;
private PropertyValues propertyValues;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
//简单的优化,避免后面 for 循环时还得判断属性填充是否为空
this.propertyValues = new PropertyValues();
}
public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
this.beanClass = beanClass;
//简单的优化,避免后面 for 循环时还得判断属性填充是否为空
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
// ...get/set
}
3.BeanReference
,Bean 的引用:
public class BeanReference {
private final String beanName;
public BeanReference(String beanName) {
this.beanName = beanName;
}
public String getBeanName() {
return beanName;
}
}
BeanReference
是用来标识一个属性依赖于另一个 Bean,容器在注入阶段会据此调用 getBean()
获取目标实例进行填充。
也就是说,BeanReference
是一个中介符号:它在注入属性时告诉容器“这里不是普通值,而是另一个 Bean,要先去 getBean”。
这个设计为容器实现依赖注入、懒加载、递归创建、循环依赖处理打下了基础,是一个关键的建模工具。
4. Bean 属性填充部分:
AbstractAutowireCapableBeanFactory内:
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
//创建实例
bean = createBeanInstance(beanDefinition, beanName, args);
//注入属性
applyPropertyValues(bean, beanName, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
.......
private void applyPropertyValues(Object bean, String beanName, BeanDefinition beanDefinition) {
//从beanDefinition得到这个bean的propertyValues。依次注入。如果要注入的属性时另一个bean,就getBean(创建或获取)。
try {
PropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
String name = propertyValue.getName();
Object value = propertyValue.getValue();
//instanceof:判断value这个对象是否为BeanReference的实例
if (value instanceof BeanReference) {
//注意暂时没有去处理循环依赖的问题,后续补充
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
// 属性填充(BeanUtil是hutool的)
BeanUtil.setFieldValue(bean, name, value);
}
} catch (Exception e) {
throw new BeansException("Error setting property values:" + beanName);
}
}
测试:
测试bean:
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
static {
hashMap.put("10001", "CMD137");
hashMap.put("10002", "CMD138");
hashMap.put("10003", "CMD139");
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
public class UserService {
private String name;
public UserService() {
// 必须有这个无参构造器,CGLIB 需要它
}
public UserService(String name) {
this.name = name;
}
private String uId;
private UserDao userDao;
public void queryUserInfo() {
System.out.println("查询用户信息:" + userDao.queryUserName(uId));
}
public String getuId() {
return uId;
}
public void setuId(String uId) {
this.uId = uId;
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("");
sb.append("").append(name);
return sb.toString();
}
}
test:
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. UserDao 注册
beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
// 3. UserService 设置属性[uId、userDao]
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));
propertyValues.addPropertyValue(new PropertyValue("userDao",new BeanReference("userDao")));
// 4. UserService 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class, propertyValues);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 5. UserService 获取bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}
结果:
查询用户信息:CMD137
Process finished with exit code 0
Step 5:资源加载器解析文件注册对象:
目标:
通过配置文件的方式将 注册bean、设置属性、对bean的注入交由框架实现。
设计:
本章节主要实现了将 Bean 的定义、注册和初始化通过 spring.xml
配置文件来处理,核心分为两部分:
资源加载部分:
实现了 Resource
和 ResourceLoader
接口,支持从类路径(Classpath)、文件系统、云配置等位置加载资源,统一由 DefaultResourceLoader
实现具体逻辑。这一设计与 Spring 源码保持一致。
XML 解析与注册部分:
- 使用
BeanDefinitionReader
接口进行资源读取与 Bean 注册。 - 接口
BeanDefinitionReader
:定义读取和注册的标准。 - 抽象类
AbstractBeanDefinitionReader
:实现通用逻辑,如加载资源、填充组件。 - 实现类
XmlBeanDefinitionReader
:专注于解析 XML 文件并注册 Bean。
Spring Bean 容器资源加载和使用类关系:
另外本章节还参考 Spring 源码,做了相应接口的集成和实现的关系,虽然这些接口目前还并没有太大的作用,但随着框架的逐步完善,它们也会发挥作用:
- BeanFactory,已经存在的 Bean 工厂接口用于获取 Bean 对象,这次新增加了按照类型获取 Bean 的方法:
<T> T getBean(String name, Class<T> requiredType)
- ListableBeanFactory,是一个扩展 Bean 工厂接口的接口,新增加了
getBeansOfType
、getBeanDefinitionNames()
方法,在 Spring 源码中还有其他扩展方法。 - HierarchicalBeanFactory,在 Spring 源码中它提供了可以获取父类 BeanFactory 方法,属于是一种扩展工厂的层次子接口。Sub-interface implemented by bean factories that can be part of a hierarchy.
- AutowireCapableBeanFactory,是一个自动化处理Bean工厂配置的接口,目前案例工程中还没有做相应的实现,后续逐步完善。
- ConfigurableBeanFactory,可获取 BeanPostProcessor、BeanClassLoader等的一个配置化接口。
- ConfigurableListableBeanFactory,提供分析和修改Bean以及预先实例化的操作接口,不过目前只有一个 getBeanDefinition 方法
实现:
0.工具类ClassUtils:
此方法通过“优先线程上下文 + 兜底类加载器”的策略,增强了兼容性和稳定性。这是 Spring 等框架中的标准做法。
public class ClassUtils {
/**
* 获取默认的类加载器
* @return 当前线程的上下文类加载器,如果获取不到,则返回加载本类的类加载器
*/
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
// 尝试获取当前线程上下文的类加载器(最常用、最灵活)
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// 获取失败时捕获异常(比如安全沙箱环境会抛异常)
// 此处不处理异常,只是 fallback 到下一个方案
}
if (cl == null) {
// 如果线程上下文类加载器为 null,则使用当前类(ClassUtils)自身的类加载器
cl = ClassUtils.class.getClassLoader();
}
// 返回获取到的类加载器
return cl;
}
}
1.资源加载接口定义和实现
public interface Resource {
InputStream getInputStream() throws IOException;
}
定义 Resource 接口,提供获取 InputStream 流的方法,接下来再分别实现三种不同的流文件操作:classPath、FileSystem、URL
ClassPath:
/**
* ClassPathResource 实现了 Resource 接口,
* 用于从 classpath(类路径)中加载资源,如配置文件。
*/
public class ClassPathResource implements Resource {
// 资源路径,例如 "application.xml"
private final String path;
// 用于加载资源的类加载器
private ClassLoader classLoader;
/**
* 构造函数,只传入路径,使用默认类加载器
* @param path 类路径下的资源路径
*/
public ClassPathResource(String path) {
// 调用另一个构造函数,显式传入 null,表示使用默认类加载器
// `(ClassLoader) null` 是一种语法写法,明确指定 null 是 ClassLoader 类型,防止类型不匹配
this(path, (ClassLoader) null);
}
/**
* 构造函数,允许指定类加载器
* @param path 资源路径
* @param classLoader 类加载器,可为空
*/
public ClassPathResource(String path, ClassLoader classLoader) {
// 使用 hutool 提供的断言工具,确保 path 不为 null
// 否则会抛出 IllegalArgumentException,提示错误信息
Assert.notNull(path, "Path must not be null");
this.path = path;
// 如果传入的 classLoader 为 null,则使用默认类加载器(由 ClassUtils 提供)
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
/**
* 获取资源的输入流,供后续解析(例如 XML 配置解析)
* @return InputStream 输入流
* @throws IOException 如果找不到资源则抛出异常
*/
@Override
public InputStream getInputStream() throws IOException {
// 使用类加载器从 classpath 中加载资源为输入流
InputStream is = classLoader.getResourceAsStream(path);
// 如果加载失败,返回 null,则抛出文件未找到异常
if (is == null) {
throw new FileNotFoundException(
this.path + " cannot be opened because it does not exist");
}
return is;
}
}
FileSystem:
通过指定文件路径的方式读取文件信息
public class FileSystemResource implements Resource {
private final File file;
private final String path;
public FileSystemResource(File file) {
this.file = file;
this.path = file.getPath();
}
public FileSystemResource(String path) {
this.file = new File(path);
this.path = path;
}
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
public final String getPath() {
return this.path;
}
}
Url:
通过 HTTP 的方式读取云服务的文件,我们也可以把配置文件放到 GitHub 或者 Gitee 上。
2.包装资源加载器:
按照资源加载的不同方式,资源加载器可以把这些方式集中到统一的类服务下进行处理,外部用户只需要传递资源地址即可,简化使用。
定义接口:
/**
* 资源加载器接口,定义统一的资源加载方式。
* 框架中所有资源(如 XML、配置文件等)都应通过该接口加载。
*/
public interface ResourceLoader {
/**
* 类路径前缀,用于识别类路径资源。
* 例如:classpath:application.xml
*/
String CLASSPATH_URL_PREFIX = "classpath:";
/**
* 根据给定路径 location 返回对应的 Resource 实例。
* 路径可以是:
* - classpath:xxx.xml(类路径)
* - file:/xxx/xxx.txt(文件系统)
* - http://xxx.com/xx.properties(网络地址)
*
* @param location 资源位置字符串
* @return 封装后的 Resource 对象(可进一步读取 InputStream)
*/
Resource getResource(String location);
}
实现接口:
public class DefaultResourceLoader implements ResourceLoader {
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (isClasspathResource(location)) {
return getClasspathResource(location);
}
if (isUrl(location)) {
return getUrlResource(location);
}
return getFileSystemResource(location);
}
private boolean isClasspathResource(String location) {
return location.startsWith(CLASSPATH_URL_PREFIX);
}
private Resource getClasspathResource(String location) {
String path = location.substring(CLASSPATH_URL_PREFIX.length());
return new ClassPathResource(path);
}
private boolean isUrl(String location) {
try {
new URL(location);
return true;
} catch (MalformedURLException e) {
return false;
}
}
private Resource getUrlResource(String location) {
try {
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException e) {
// 理论上这里不会异常,因为已经判断过了
throw new IllegalArgumentException("Invalid URL: " + location, e);
}
}
private Resource getFileSystemResource(String location) {
return new FileSystemResource(location);
}
}
3. Bean定义读取接口
/**
* BeanDefinitionReader 是负责读取 Bean 定义的接口,
* 其核心功能是从各种资源中加载 Bean 的配置信息,
* 并将 BeanDefinition 注册到 BeanDefinitionRegistry 中。
*/
public interface BeanDefinitionReader {
/**
* 获取注册 BeanDefinition 的注册表
* @return BeanDefinitionRegistry 实例
*/
BeanDefinitionRegistry getRegistry();
/**
* 获取资源加载器,负责定位和加载资源文件
* @return ResourceLoader 实例
*/
ResourceLoader getResourceLoader();
/**
* 从单个 Resource(资源)中加载 Bean 定义
* @param resource 资源对象,如 classpath 下的 XML 配置文件
* @throws BeansException 加载异常
*/
void loadBeanDefinitions(Resource resource) throws BeansException;
/**
* 从多个 Resource(资源)中批量加载 Bean 定义
* @param resources 资源数组(可变参数),支持传入多个 Resource
* @throws BeansException 加载异常
*/
void loadBeanDefinitions(Resource... resources) throws BeansException;
/**
* 从资源路径字符串(location)加载 Bean 定义
* @param location 资源路径,如 "classpath:beans.xml"
* @throws BeansException 加载异常
*/
void loadBeanDefinitions(String location) throws BeansException;
/**
* 从多个资源路径字符串(location)批量加载 Bean 定义
* @param locations 资源路径数组(可变参数),支持传入多个 Resource
* @throws BeansException 加载异常
*/
void loadBeanDefinitions(String... locations) throws BeansException;
}
另外:Resource... resources
是用法?
Resource... resources
是 Java 中的**可变参数(varargs)**语法,表示该方法可以接收任意数量的Resource
对象,甚至可以不传。- 调用时可以这样用:
java复制编辑loadBeanDefinitions(res1);
loadBeanDefinitions(res1, res2, res3);
loadBeanDefinitions(); // 传空参数也合法(具体看实现)
编译器会把可变参数当成数组处理,方法内部可以像处理数组一样访问 resources
。
4.Bean定义抽象类实现
小知识点:抽象类实现接口可以不全部实现。
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
private final BeanDefinitionRegistry registry;
private ResourceLoader resourceLoader;
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, new DefaultResourceLoader());
}
public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
this.registry = registry;
this.resourceLoader = resourceLoader;
}
@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}
@Override
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
抽象类把 BeanDefinitionReader 接口的前两个方法全部实现完了,并提供了构造函数,让外部的调用使用方,把Bean定义注入类传递进来。
5.补充containsBeanDefinition:
在BeanDefinitionRegistry这个接口中添加定义:
/**
* 判断是否包含指定名称的BeanDefinition
* @param beanName
* @return
*/
boolean containsBeanDefinition(String beanName);
在实现类DefaultListableBeanFactory中实现:
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
6.实现解析XML处理Bean注册类:
/**
* XmlBeanDefinitionReader 继承自 AbstractBeanDefinitionReader,
* 负责从 XML 资源中读取并解析 Bean 定义信息,
* 然后注册到 BeanDefinitionRegistry 中。
*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
/**
* 构造函数,只传入 BeanDefinition 注册表,使用默认资源加载器
* @param registry BeanDefinition 注册表
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
/**
* 构造函数,传入 BeanDefinition 注册表和资源加载器
* @param registry BeanDefinition 注册表
* @param resourceLoader 资源加载器
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}
/**
* 从单个 Resource 资源中加载 Bean 定义
* @param resource 资源,如 classpath 下的 XML 文件
* @throws BeansException 加载或解析异常
*/
@Override
public void loadBeanDefinitions(Resource resource) throws BeansException {
try {
// 通过 try-with-resources 自动关闭输入流
try (InputStream inputStream = resource.getInputStream()) {
// 委托给具体的加载方法进行 XML 解析
doLoadBeanDefinitions(inputStream);
}
} catch (IOException | ClassNotFoundException e) {
// 包装异常,抛出自定义 BeansException
throw new BeansException("IOException parsing XML document from " + resource, e);
}
}
/**
* 批量从多个 Resource 资源中加载 Bean 定义
* @param resources 多个资源数组(可变参数)
* @throws BeansException 加载异常
*/
@Override
public void loadBeanDefinitions(Resource... resources) throws BeansException {
for (Resource resource : resources) {
// 循环调用单个资源加载方法
loadBeanDefinitions(resource);
}
}
/**
* 通过资源路径字符串加载 Bean 定义
* @param location 资源路径,如 "classpath:beans.xml"
* @throws BeansException 加载异常
*/
@Override
public void loadBeanDefinitions(String location) throws BeansException {
ResourceLoader resourceLoader = getResourceLoader();
// 使用资源加载器将路径转成 Resource 对象
Resource resource = resourceLoader.getResource(location);
// 调用 Resource 版本的加载方法
loadBeanDefinitions(resource);
}
/**
* 批量从多个资源路径字符串中加载 Bean 定义
* @param locations 多个资源路径字符串数组
* @throws BeansException 加载异常
*/
@Override
public void loadBeanDefinitions(String... locations) throws BeansException {
for (String location : locations) {
loadBeanDefinitions(location);
}
}
/**
* 解析 XML 输入流,读取 Bean 定义信息并注册
* @param inputStream XML 配置文件输入流
* @throws ClassNotFoundException 找不到指定类异常
*/
protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
// 解析 XML 文档
Document doc = XmlUtil.readXML(inputStream);
Element root = doc.getDocumentElement();
NodeList childNodes = root.getChildNodes();
// 遍历根节点的所有子节点
for (int i = 0; i < childNodes.getLength(); i++) {
// 过滤非 Element 类型节点
if (!(childNodes.item(i) instanceof Element)) continue;
// 只处理 <bean> 标签
if (!"bean".equals(childNodes.item(i).getNodeName())) continue;
// 强转成 Element 方便操作
Element bean = (Element) childNodes.item(i);
// 获取 bean 的 id、name、class 属性
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
// 加载 bean 对应的 Class 对象
Class<?> clazz = Class.forName(className);
// 确定 bean 名称,优先使用 id,再使用 name,最后默认使用类名首字母小写
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
// 创建 BeanDefinition,保存 Class 信息
BeanDefinition beanDefinition = new BeanDefinition(clazz);
// 解析 <property> 子标签,填充 Bean 属性信息
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (!(bean.getChildNodes().item(j) instanceof Element)) continue;
if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) continue;
Element property = (Element) bean.getChildNodes().item(j);
String attrName = property.getAttribute("name");
String attrValue = property.getAttribute("value");
String attrRef = property.getAttribute("ref");
// 判断属性值是普通值还是引用其他 Bean
Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
// 创建 PropertyValue 对象,封装属性名和值
PropertyValue propertyValue = new PropertyValue(attrName, value);
// 添加到 BeanDefinition 中的属性集合
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
// 判断是否有重复 BeanName,防止覆盖
if (getRegistry().containsBeanDefinition(beanName)) {
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
// 注册 BeanDefinition 到注册表中
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
7.其它补充和优化:
BeanFactory:
添加:
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
ListableBeanFactory,是一个扩展 Bean 工厂接口的接口:
/**
* 在只有getBean()的BeanFactory基础上,增加了批量查询和列出所有 Bean 的功能
*/
public interface ListableBeanFactory extends BeanFactory{
/**
* 按照类型返回 Bean 实例
* @param type
* @param <T>
* @return
* @throws BeansException
*/
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
/**
* Return the names of all beans defined in this registry.
*
* 返回注册表中所有的Bean名称
*/
String[] getBeanDefinitionNames();
}
HierarchicalBeanFactory:提供了可以获取父类 BeanFactory 方法
/**
* 由可以成为层级结构一部分的 BeanFactory 实现的子接口
*/
public interface HierarchicalBeanFactory extends BeanFactory{
}
ConfigurableBeanFactory,可获取 BeanPostProcessor、BeanClassLoader等的一个配置化接口:
/**
* 配置型 Bean 工厂接口,通常由大多数 BeanFactory 实现。
*
* 它在 BeanFactory 的基础上,扩展了对 Bean 工厂的配置能力,例如:
* - 设置作用域(singleton / prototype)
* - 注册单例 Bean
* - 支持层级结构(父子容器)
*
* 继承了:
* - HierarchicalBeanFactory:提供父容器支持,可通过 getParentBeanFactory() 获取父容器。
* - SingletonBeanRegistry:允许手动注册/获取单例对象。
*/
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
/** 单例作用域:容器中只创建一个共享的 Bean 实例 */
String SCOPE_SINGLETON = "singleton";
/** 原型作用域:每次获取 Bean 都会创建一个新的实例 */
String SCOPE_PROTOTYPE = "prototype";
}
AutowireCapableBeanFactory,是一个自动化处理Bean工厂配置的接口,目前案例工程中还没有做相应的实现,后续逐步完善:
/**
* AutowireCapableBeanFactory(自动装配能力的 Bean 工厂)
*
* 这是 beans.factory.BeanFactory 的扩展接口,
* 用于具备“自动装配现有 Bean 实例”能力的工厂。
*
* 通常用于:
* - 将一个**已经存在的对象**(不是容器创建的)注入其依赖项(属性、注解等)。
* - 在运行时对对象进行依赖注入、生命周期管理(如调用初始化方法)。
*
* 举例场景:
* - 手动 new 出一个对象,然后交给 Spring 去完成依赖注入(而不是由容器创建)
*
* 在 Spring 中的实现类:
* - DefaultListableBeanFactory 就实现了这个接口。
*/
public interface AutowireCapableBeanFactory extends BeanFactory {
}
ConfigurableListableBeanFactory,提供分析和修改Bean以及预先实例化的操作接口,不过目前只有一个 getBeanDefinition 方法:
/**
* 可配置且可列举的 Bean 工厂接口。
*
* 此接口通常由支持列出所有 Bean 定义的 Bean 工厂实现。
* 相较于 {@link ConfigurableBeanFactory},它还提供了分析和修改 BeanDefinition、
* 以及预实例化单例 Bean 的能力。
*
* Spring 容器刷新(refresh)过程中,通常会用这个接口对 BeanDefinition 做统一处理。
*/
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, // 可以按类型、名称列出 Bean
AutowireCapableBeanFactory, // 提供自动装配已存在对象的能力
ConfigurableBeanFactory { // 提供作用域、自定义类型转换器、注册钩子等能力
/**
* 获取指定名称的 BeanDefinition 对象。
*
* @param beanName Bean 的名称
* @return BeanDefinition 定义信息(如:class、作用域、依赖属性等)
* @throws BeansException 如果未找到对应的定义
*/
BeanDefinition getBeanDefinition(String beanName) throws BeansException;
}
DefaultListableBeanFactory实现ConfigurableListableBeanFactory接口
添加了:
/**
* 根据指定类型获取所有匹配的Bean实例映射
*
* @param type 要获取的Bean类型
* @param <T> 类型参数,限定返回值的类型
* @return 包含所有匹配类型Bean的Map,键为Bean名称,值为Bean实例
* @throws BeansException 当获取Bean过程中发生异常时抛出
*/
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
// 创建用于存储结果的Map
Map<String, T> result = new HashMap<>();
// 遍历所有已注册的Bean定义
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
// 获取当前Bean定义对应的Class对象
Class beanClass = beanDefinition.getBeanClass();
// 判断当前Bean的类型是否与目标类型兼容
// isAssignableFrom()方法用于判断当前Class对象所表示的类或接口是否与指定的Class参数表示的类或接口相同,或是否是其超类/超接口
// 即:type是否是beanClass的父类/父接口,或者两者是同一个类
if (type.isAssignableFrom(beanClass)) {
result.put(beanName, (T) getBean(beanName));
}
});
return result;
}
AbstractBeanFactory:
实现getBean泛型重载,被DefaultListableBeanFactory继承。
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return (T) getBean(name);
}
测试:
配置文件:
important.properties:用于测试资源加载器
# Config File
system.key=OLpj9823dZ
spring.xml:用于测试整体的 Bean 注册功能
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
1.单元测试(资源加载):
private DefaultResourceLoader resourceLoader;
@Before
public void init() {
resourceLoader = new DefaultResourceLoader();
}
@Test
public void test_classpath() throws IOException {
Resource resource = resourceLoader.getResource("classpath:important.properties");
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}
@Test
public void test_file() throws IOException {
Resource resource = resourceLoader.getResource("src/test/resources/important.properties");
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}
@Test
public void test_url() throws IOException {
// 网络原因可能导致GitHub不能读取,可以放到自己的Gitee仓库。读取后可以从内容中搜索关键字;OLpj9823dZ
Resource resource = resourceLoader.getResource("https://github.com/fuzhengwei/small-spring/blob/main/important.properties")
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}
结果:
# Config File
system.key=OLpj9823dZ
Process finished with exit code 0
12 单元测试(配置文件注册Bean):
@Test
public void test_xml() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. 读取配置文件&注册Bean
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:spring.xml");
// 3. 获取Bean对象调用方法
UserService userService = beanFactory.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}
结果:
测试结果:CMD137
Step 6:实现应用上下文:
目标:
- 封装统一的上下文入口(ApplicationContext),把 Bean 对象扩展机制功能和对 Spring 框架上下文的包装融合起来,对外提供完整的服务;
- 引入扩展机制,支持用户自定义 Bean 修改与增强;
- 实现 Bean 生命周期中关键扩展点的插入逻辑;
知识:
先明确Bean的生命周期:
Bean 生命周期阶段(含扩展点):⭐⭐⭐⭐⭐
- 容器启动
- 加载 & 注册 BeanDefinition
- 🔧 调用 BeanFactoryPostProcessor【修改 BeanDefinition】
- 实例化 Bean(new 对象)
- 属性填充(依赖注入)
- 感知容器:调用 Aware 接口
- 初始化 Bean(初始化阶段)
├─ 🔧 BeanPostProcessor.beforeInit()
├─ InitializingBean.afterPropertiesSet()
├─ init-method
└─🔧 BeanPostProcessor.afterInit() - Bean 可用(getBean 获取)
- 销毁(DisposableBean / destroy-method)
设计:
1. ApplicationContext 的封装
设计一个新的上下文类 ClassPathXmlApplicationContext
,它作为对外暴露的容器接口,负责整合以下职责:
- 加载和解析 XML 配置;
- 注册 BeanDefinition;
- 管理 BeanFactory;
- 调用扩展接口;
- 提供 Bean 获取入口;
- 实例化 Bean。
此类将成为我们实现注解扫描、事件发布、AOP 等高级特性的基础。
2. 支持两种扩展机制接口
为了增强 Bean 的定义和实例化过程,我们引入两个与 Spring 完全一致的扩展接口:
(1)BeanFactoryPostProcessor
- 执行时机:在所有 BeanDefinition 加载完成后,Bean 实例化前。
- 功能作用:可修改 Bean 的配置信息(如属性值、Scope、是否懒加载等)。
- 常见用途:注入动态配置、扩展属性、修改注册策略等。
(2)BeanPostProcessor
- 执行时机:在每个 Bean 实例化后、初始化前后。
- 功能作用:可增强 Bean 实例,如代理包装、日志注入、AOP 实现等。
- 常见用途:实现代理、数据源路由、事务控制、权限注入等。
3. 自动注册与执行
- 上下文启动时自动识别 XML 配置中的扩展接口 Bean;
- 调用
postProcessBeanFactory()
修改 BeanDefinition; - 调用
postProcessBefore/AfterInitialization()
增强 Bean 实例; - 支持多个扩展类协同工作。
Spring 应用上下文和对Bean对象扩展机制的类关系:
- 以继承了 ListableBeanFactory 接口的 ApplicationContext 接口开始,扩展出一系列应用上下文的抽象实现类,并最终完成
ClassPathXmlApplicationContext
类的实现。而这个类就是最后交给用户使用的类。 - 同时在实现应用上下文的过程中,通过定义接口:
BeanFactoryPostProcessor
、BeanPostProcessor
两个接口,把关于对 Bean 的扩展机制串联进去了
实现:
1.定义 BeanFactoryPostProcessor:
public interface BeanFactoryPostProcessor {
/**
* 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制
*
* @param beanFactory
* @throws BeansException
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
2.定义 BeanPostProcessor:
public interface BeanPostProcessor {
/**
* 在 Bean 对象执行初始化方法之前,执行此方法
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* 在 Bean 对象执行初始化方法之后,执行此方法
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
3.定义上下文接口:
/**
* ApplicationContext 为容器的核心接口(Central接口),负责统一对外提供容器功能。
*/
public interface ApplicationContext extends ListableBeanFactory {
}
目前这个接口还很简化,没有增加获取上下文 ID 或父上下文等额外方法,所以接口内暂时没有新增定义。
/**
* 提供了对 Spring 容器的配置和刷新能力,是启动和重置容器的关键接口。
* 它让 ApplicationContext 不仅是“只读”的 Bean 工厂,更是可控制生命周期的完整容器。
*/
public interface ConfigurableApplicationContext extends ApplicationContext {
/**
* 刷新(启动)容器
*
* @throws BeansException
*/
void refresh() throws BeansException;
}
此处有个小问题:为什么 Spring 里用 refresh() 而不是 start() 来表示“启动容器”的动作?
简单理解:refresh()
强调容器可重建、可热加载(在整个spring服务在没有关闭的情况下,作为核心的容器可以单独刷新),而 start()
更像是一次性的启动,不符合 Spring 容器的设计理念。
4.应用上下文抽象类实现前提:
在实现应用上下文抽象类之前,先要补充上一节拓展的一些接口:
注意!!!!!!!!!此处教程中在ConfigurableBeanFactory及其子接口ConfigurableListableBeanFactory中都定义了addBeanPostProcessor,重复定义了,于是查看源代码,发现addBeanPostProcessor只定义在ConfigurableBeanFactory中。此处按spring源码实现。
ConfigurableBeanFactory:
添加方法:
/**
* 注册 BeanPostProcessor,用于在 Bean 初始化前后进行扩展处理。
* 所有注册的后置处理器会在创建每个 Bean 时自动调用。
*/
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
ConfigurableListableBeanFactory:
添加方法:
/**
* 实例化所有非懒加载的单例 Bean,确保容器启动后这些 Bean 可用。
* 会触发完整的 Bean 创建流程(实例化、依赖注入、初始化等)。
*/
void preInstantiateSingletons() throws BeansException;
对应的,实现类里就要实现:
AbstractBeanFactory:
修改:implements BeanFactory -》implements ConfigurableBeanFactory;
添加:
// 存储所有注册的 BeanPostProcessor,用于在 Bean 初始化前后进行扩展处理
private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
/**
* 注册一个 BeanPostProcessor。
* 若已存在相同实例,则先移除后再添加,确保顺序性(后添加的在最后执行)。
*/
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
// 避免重复注册同一个处理器
this.beanPostProcessors.remove(beanPostProcessor);
// 添加到列表末尾,确保调用顺序
this.beanPostProcessors.add(beanPostProcessor);
}
/**
*返回将在该工厂创建的 Bean 上应用的 BeanPostProcessor 列表。
*/
public List<BeanPostProcessor> getBeanPostProcessors() {
return this.beanPostProcessors;
}
DefaultListableBeanFactory:添加
@Override
public void preInstantiateSingletons() throws BeansException {
for (String beanName : beanDefinitionMap.keySet()) {
getBean(beanName);
}
}
4.应用上下文抽象类实现:
AbstractApplicationContext
是一个抽象类,作为 应用上下文容器的骨架实现,统一定义了 Spring 容器初始化的标准流程 refresh()
,并将具体细节延迟到子类中实现(如 refreshBeanFactory()
、getBeanFactory()
)。
方法核心流程:
refresh() {
① refreshBeanFactory(); // 创建 BeanFactory 并加载 BeanDefinition
② getBeanFactory(); // 获取容器的 BeanFactory
③ invokeBeanFactoryPostProcessors(); // 执行 BeanFactoryPostProcessor,修改 Bean 定义
④ registerBeanPostProcessors(); // 注册 BeanPostProcessor,用于 Bean 创建过程中的拦截
⑤ preInstantiateSingletons(); // 实例化所有非懒加载的单例 Bean
}
步骤 | 作用 | 扩展点 |
---|---|---|
① 创建 BeanFactory | 通常是 DefaultListableBeanFactory,加载并注册所有 BeanDefinition | 支持不同配置来源 |
② 获取 BeanFactory | 提供后续 Bean 的注册、创建、获取等操作入口 | —— |
③ 执行 BeanFactoryPostProcessor | 允许在 Bean 实例化前,修改 BeanDefinition(比如替换属性值、动态注册新 Bean) | BeanFactory 扩展点 |
④ 注册 BeanPostProcessor | 在 Bean 创建过程(初始化前后)插入逻辑,如代理、注入等 | Bean 生命周期扩展点 |
⑤ 实例化单例 Bean | 调用 createBean() 流程,包括依赖注入、初始化方法、AOP等 | Bean 的完整生命周期 |
AbstractApplicationContext 继承 DefaultResourceLoader 是为了处理 spring.xml
配置资源的加载。
/**
* 抽象的应用上下文,实现了容器的刷新机制,是容器启动的核心入口。
* 继承 DefaultResourceLoader,具备资源加载能力;实现 ConfigurableApplicationContext,可配置上下文。
*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition(从配置文件或注解中解析出 Bean 定义)
refreshBeanFactory();
// 2. 获取当前上下文使用的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 执行所有 BeanFactoryPostProcessor,允许修改 BeanDefinition
// (此时还未实例化任何 Bean)
invokeBeanFactoryPostProcessors(beanFactory);
// 4. 注册所有 BeanPostProcessor,为后续 Bean 实例化过程添加扩展逻辑(如 AOP、依赖注入等)
registerBeanPostProcessors(beanFactory);
// 5. 提前实例化所有非懒加载的单例 Bean(触发完整的 Bean 创建流程)
beanFactory.preInstantiateSingletons();
}
/**
* 刷新 BeanFactory 的抽象方法,由子类实现,负责创建 BeanFactory 和加载 BeanDefinition。
*/
protected abstract void refreshBeanFactory() throws BeansException;
/**
* 获取当前上下文持有的 BeanFactory,由子类实现。
*/
protected abstract ConfigurableListableBeanFactory getBeanFactory();
/**
* 执行所有注册的 BeanFactoryPostProcessor,允许用户在 Bean 实例化前修改 BeanDefinition。
*/
private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 获取所有实现了 BeanFactoryPostProcessor 的 Bean
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap =
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
// 依次执行每个 BeanFactoryPostProcessor 的后置处理方法
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}
/**
* 注册所有 BeanPostProcessor,在 Bean 创建过程中进行扩展处理(如 AOP、属性注入、代理包装等)。
* BeanPostProcessor 会作用于每一个后续创建的 Bean。
*/
private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 获取所有 BeanPostProcessor 类型的 Bean
Map<String, BeanPostProcessor> beanPostProcessorMap =
beanFactory.getBeansOfType(BeanPostProcessor.class);
// 将它们注册到 BeanFactory 中,便于后续在 Bean 创建过程中回调
for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
}
// 以下是对 ApplicationContext 中部分 getBean 方法的默认实现,均委托给 BeanFactory
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
}
@Override
public String[] getBeanDefinitionNames() {
return getBeanFactory().getBeanDefinitionNames();
}
@Override
public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return getBeanFactory().getBean(name, args);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
}
}
5.可获取Bean工厂和加载资源的上下文实现类:AbstractRefreshableApplicationContext
/**
* 抽象的可刷新的应用上下文实现类。
* 获取了 DefaultListableBeanFactory 的实例化
* 定义了对资源配置的加载操作 loadBeanDefinitions(beanFactory)。
*/
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
// 持有当前上下文使用的 BeanFactory 实例
private DefaultListableBeanFactory beanFactory;
/**
* 刷新 BeanFactory:每次 refresh() 时都会创建新的 BeanFactory 并加载 BeanDefinition。
*/
@Override
protected void refreshBeanFactory() throws BeansException {
// 创建一个新的 BeanFactory 实例
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 将配置信息(如 XML 或注解)加载到新创建的 BeanFactory 中
loadBeanDefinitions(beanFactory);
// 用新 BeanFactory 替换旧的(相当于完成一次“上下文刷新”)
this.beanFactory = beanFactory;
}
/**
* 创建一个新的 BeanFactory 实例。
*/
private DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory();
}
/**
* 抽象方法,由子类实现,用于加载 BeanDefinition(即 Bean 的配置信息)。
* 例如:XmlApplicationContext 会从 XML 中解析 BeanDefinition。
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory);
/**
* 获取当前应用上下文持有的 BeanFactory。
*/
@Override
protected ConfigurableListableBeanFactory getBeanFactory() {
return beanFactory;
}
}
6.对配置信息的加载的实现:
/**
* 基于 XML 配置文件的抽象应用上下文,实现了从配置路径中加载 Bean 定义的功能。
* 继承自 AbstractRefreshableApplicationContext,具备“可刷新 BeanFactory”的能力。
*
* 该类的主要职责是调用 XmlBeanDefinitionReader 从 XML 文件中读取 BeanDefinition。
* 子类需要提供配置文件的路径(如 ClassPathXmlApplicationContext)。
*/
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {
/**
* 加载 Bean 定义:使用 XmlBeanDefinitionReader 解析 XML 配置文件,
* 并将解析结果注册到传入的 BeanFactory 中。
*
* @param beanFactory 用于注册解析后的 Bean 定义
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 创建 XML 解析器,传入 BeanFactory 和资源加载器(ApplicationContext 本身)
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this);
// 获取配置文件路径
String[] configLocations = getConfigLocations();
// 加载 XML 配置中的 Bean 定义
if (null != configLocations){
beanDefinitionReader.loadBeanDefinitions(configLocations);
}
}
/**
* 获取配置文件路径(如 classpath:spring.xml)
* 该方法交由子类实现,允许灵活指定不同的配置资源。
*
* @return 包含配置文件路径的字符串数组
*/
protected abstract String[] getConfigLocations();
}
7.应用上下文实现类:ClassPathXmlApplicationContext:
/**
* 应用上下文实现类,从类路径下的 XML 文件中加载 Bean 定义。
* 继承自 AbstractXmlApplicationContext,具体实现了配置路径的获取方式。
*
* 一旦构造方法接收到配置路径,会立即调用 refresh() 方法,完成整个 IoC 容器的创建与初始化流程。
*/
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
// 配置文件路径列表(如 "classpath:spring.xml")
private String[] configLocations;
public ClassPathXmlApplicationContext() {
}
/**
* 构造函数:接收单个配置路径,转换为数组形式
* 并立即触发上下文刷新流程(加载 BeanDefinition + 初始化容器)
*
* @param configLocations XML 配置文件路径(单个)
* @throws BeansException 异常处理
*/
public ClassPathXmlApplicationContext(String configLocations) throws BeansException {
this(new String[]{configLocations});
}
/**
* 构造函数:接收多个配置路径
* 并立即触发上下文刷新流程(加载 BeanDefinition + 初始化容器)
*
* @param configLocations XML 配置文件路径数组
* @throws BeansException 异常处理
*/
public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
// 设置配置路径
this.configLocations = configLocations;
// 刷新容器:完成 BeanFactory 创建、加载配置、注册 Bean 等一系列动作
refresh();
}
/**
* 提供配置文件路径给抽象父类用于加载 BeanDefinition
*
* @return 配置路径数组
*/
@Override
protected String[] getConfigLocations() {
return configLocations;
}
}
流程说明
ClassPathXmlApplicationContext
构造方法- 接收
configLocations
(XML 配置路径)字符串或数组。 - 构造完成后立即调用
refresh()
方法初始化上下文。
- 接收
refresh()
(定义于AbstractApplicationContext
)- 是整个上下文刷新过程的核心方法,统一控制整个 Spring 容器初始化流程。
- 内部会调用
refreshBeanFactory()
和其他注册、实例化相关操作。
refreshBeanFactory()
(定义于AbstractRefreshableApplicationContext
)- 创建一个新的 BeanFactory(通常是
DefaultListableBeanFactory
)。 - 调用
loadBeanDefinitions()
读取并注册 XML 中定义的 Bean。
- 创建一个新的 BeanFactory(通常是
loadBeanDefinitions()
(定义于AbstractXmlApplicationContext
)- 实例化
XmlBeanDefinitionReader
。 - 调用
getConfigLocations()
获取 XML 文件路径。
- 实例化
getConfigLocations()
(实现于ClassPathXmlApplicationContext
)- 返回构造时传入的配置路径数组。
XmlBeanDefinitionReader.loadBeanDefinitions()
- 读取 XML 文件并将其中定义的 Bean 信息注册到 BeanFactory 中
8.在Bean创建时完成前置和后置处理:
补充AbstractAutowireCapableBeanFactory这个Bean生命周期管理的核心实现类。
首先要填前面的坑:
补充AutowireCapableBeanFactory接口,然后让AbstractAutowireCapableBeanFactory实现。
public interface AutowireCapableBeanFactory extends BeanFactory {
/**
* 执行 BeanPostProcessors 接口实现类的 postProcessBeforeInitialization 方法
*
* @param existingBean
* @param beanName
* @return
* @throws BeansException
*/
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
/**
* 执行 BeanPostProcessors 接口实现类的 postProcessorsAfterInitialization 方法
*
* @param existingBean
* @param beanName
* @return
* @throws BeansException
*/
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
}
AbstractAutowireCapableBeanFactory:
修改和补充:
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
//创建实例
bean = createBeanInstance(beanDefinition, beanName, args);
//注入属性
applyPropertyValues(bean, beanName, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
/**
*初始化 Bean:执行初始化前后处理器、调用初始化方法invokeInitMethods
*/
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 1. 执行 BeanPostProcessor Before 处理
// 比如:@PostConstruct 或 Aware 接口处理可以在这里插入
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);
invokeInitMethods(beanName, wrappedBean, beanDefinition);
// 2. 执行 BeanPostProcessor After 处理
// 比如:@PostConstruct 或 Aware 接口处理可以在这里插入
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
// 返回最终初始化完成的 Bean
return wrappedBean;
}
/**
* 调用 Bean 的初始化方法
*/
private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) {
//TODO:后续补充
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (null == current) return result;
result = current;
}
return result;
}
/*
* 执行所有注册的 BeanPostProcessor 的 postProcessBeforeInitialization 方法
* 用于在 Bean 初始化方法执行前,做一些预处理操作
*/
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (null == current) return result;
result = current;
}
return result;
}
测试:
除了经典的UserDao和UserService(新增加了 company、location,两个属性信息,便于测试 BeanPostProcessor、BeanFactoryPostProcessor 两个接口对 Bean 属性信息扩展的作用),添加:
实现 BeanPostProcessor 和 BeanFactoryPostProcessor:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
UserService userService = (UserService) bean;
userService.setLocation("改为:北京");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
PropertyValues propertyValues = beanDefinition.getPropertyValues();
propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));
}
}
配置文件:
基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
增强配置,有BeanFactoryPostProcessor、BeanPostProcessor,实现类:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>
<bean class="cn.bugstack.springframework.test.common.MyBeanPostProcessor"/>
<bean class="cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor"/>
</beans>
这里提供了两个配置文件,一个是不包含BeanFactoryPostProcessor、BeanPostProcessor,另外一个是包含的。之所以这样配置主要对照验证,在运用 Spring 新增加的应用上下文和不使用的时候,都是怎么操作的。
不用应用上下文:
@Test
public void test_BeanFactoryPostProcessorAndBeanPostProcessor(){
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. 读取配置文件&注册Bean
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:spring.xml");
// 3. BeanDefinition 加载完成 & Bean实例化之前,修改 BeanDefinition 的属性值
MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
// 4. Bean实例化之后,修改 Bean 属性信息
MyBeanPostProcessor beanPostProcessor = new MyBeanPostProcessor();
beanFactory.addBeanPostProcessor(beanPostProcessor);
// 5. 获取Bean对象调用方法
UserService userService = beanFactory.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}
测试结果:
测试结果:CMD137,改为:字节跳动,改为:北京
Process finished with exit code 0
通过测试结果可以看到,我们配置的属性信息已经与 spring.xml 配置文件中不一样了。
使用应用上下文:
@Test
public void test_xml() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml");
// 2. 获取Bean对象调用方法
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}
测试结果:
测试结果:CMD137,改为:字节跳动,改为:北京
Process finished with exit code 0
使用新增加的 ClassPathXmlApplicationContext 应用上下文类,再操作起来就方便多了,这才是面向用户使用的类,在这里可以一步把配置文件交给 ClassPathXmlApplicationContext,也不需要管理一些自定义实现的 Spring 接口的类。
Step 7:初始化和销毁方法:
目标:
Spring 容器管理 Bean 时,希望让用户能在 Bean 创建与销毁的特定时机执行自定义动作,比如数据加载、资源清理或连接关闭。这些逻辑如果交由 Spring 管理,会更加优雅、安全。
设计:
两种方式声明初始化/销毁逻辑
- 接口方式:实现
InitializingBean.afterPropertiesSet()
与DisposableBean.destroy()
。 - 配置方式:在 XML 中使用
init-method="…”
与destroy-method="…”
,Spring 通过反射调用对应方法。
两种方式都会实现。
初始化逻辑:
当 Bean 完成实例化与依赖注入后,Spring 会注册初始化回调,这里不需要像销毁那样保存到 disposableBeans
,而是直接在当前创建流程里调用。
因为接口调用(afterPropertiesSet
)和反射调用(init-method
)是两种方式,所以 Spring 也有一个适配器类,在 mini-spring 里叫 InitializingBeanAdapter
(在真 Spring 里是 invokeInitMethods
里的逻辑),它会统一处理:
- 如果 Bean 实现了
InitializingBean
,直接调用afterPropertiesSet()
- 如果配置了
init-method
,通过反射调用对应方法 - 两者都存在时,接口调用优先,反射调用后执行
销毁逻辑:
销毁注册:
Bean 创建完以后,如果它有销毁逻辑,Spring 会把它注册到 DefaultSingletonBeanRegistry
的 disposableBeans
容器里:
private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();
注册的过程:
- Spring 检查这个 Bean 是否实现了
DisposableBean
,或者定义了destroy-method
。 - 如果有,则用
DisposableBeanAdapter
封装成一个统一的DisposableBean
对象。 - 把这个适配器对象放进
disposableBeans
里,key 是 beanName。
这样做的好处:
后续销毁时,只要遍历disposableBeans
,依次调用.destroy()
,不需要管具体是接口还是反射。
触发销毁(关闭容器时)
销毁有两种触发方式:
- 自动触发:注册 JVM 钩子(
Runtime.getRuntime().addShutdownHook(...)
)- 当 JVM 正常退出时会自动调用容器的
close()
方法,从而执行销毁逻辑。
- 当 JVM 正常退出时会自动调用容器的
- 手动触发:显式调用
ApplicationContext.close()
。
无论哪种方式,都会进入 AbstractApplicationContext#doClose()
→ DefaultSingletonBeanRegistry#destroySingletons()
:
- 遍历
disposableBeans
,依次调用适配器的destroy()
方法。 - 适配器内部:
- 如果实现了
DisposableBean
,调用destroy()
。 - 如果配置了
destroy-method
,用反射调用这个方法。
- 如果实现了
简化为:
初始化:
- Bean 创建完毕 → 属性填充完成
- 调用
afterPropertiesSet()
(接口方式) - 调用
init-method
方法(配置方式)
销毁:
- Bean 实例被注册进
disposableBeans
- 关闭容器 → 遍历
disposableBeans
- 调用
destroy()
(接口方式) - 调用
destroy-method
(配置方式)
Spring 应用上下文和对Bean对象扩展机制的类关系:
此处Spring 在接口实现上的一种“分层解耦”设计比较特殊(不是常见的“谁实现接口谁自己写实现代码”模式):
ConfigurableBeanFactory(接口,定义 destroySingletons 方法)
↑
AbstractBeanFactory(抽象类,没实现 destroySingletons)
↑
DefaultSingletonBeanRegistry(父类,实现了 destroySingletons)
简单演示:
// 定义能力
public interface ConfigurableBeanFactory {
void destroySingletons();
}
// 抽象类:实现了很多通用 BeanFactory 逻辑,但不管销毁细节
public abstract class AbstractBeanFactory implements ConfigurableBeanFactory {
// 这里 destroySingletons 没有实现
}
// 真正实现销毁逻辑的类(同时也是 AbstractBeanFactory 的父类)
public class DefaultSingletonBeanRegistry {
@Override
public void destroySingletons() {
// 遍历单例池,调用 destroy 方法
}
}
关键点:接口方法的实现不在直接的实现类里,而是在这个实现类的父类中完成。
为什么这样设计?
这是 Spring 分层隔离职责 的体现:
层级 | 主要职责 | 为什么放在这里 |
---|---|---|
ConfigurableBeanFactory (接口) | 只定义“BeanFactory 需要支持的可配置能力”,比如销毁 | 职责单一,告诉外界“我能干啥” |
AbstractBeanFactory (抽象类) | 提供通用的 Bean 创建、获取逻辑,不关心具体销毁细节 | 保持高内聚,不掺杂单例注册/销毁的实现 |
DefaultSingletonBeanRegistry (父类) | 只管理单例池的增删改查 & 销毁 | 销毁逻辑和单例管理放一起,解耦 BeanFactory 核心逻辑 |
实现:
1.定义初始化和销毁方法的接口
// 初始化接口
public interface InitializingBean {
/**
* Bean 处理了属性填充后调用
*/
void afterPropertiesSet() throws Exception;
}
// 销毁接口
public interface DisposableBean {
// 销毁方法
void destroy() throws Exception;
}
2.Bean属性定义新增初始化和销毁:
public class BeanDefinition {
private Class beanClass;
private PropertyValues propertyValues;
private String initMethodName;
private String destroyMethodName;
// ...get/set
}
这两个属性是为了在 spring.xml 配置的 Bean 对象中,可以配置 init-method="initDataMethod" destroy-method="destroyDataMethod"
操作。
Bean属性定义增加初始化和销毁后,还需要在XmlBeanDefinitionReader
类中,增加对新增属性的读取,并添加到BeanDefinition中:
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
......
//doLoadBeanDefinitions中增加对init-method、destroy-method的读取
protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException{
Document doc = XmlUtil.readXML(inputStream);
Element root = doc.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (!(childNodes.item(i) instanceof Element)) {
continue;
}
if (!"bean".equals(childNodes.item(i).getNodeName())) {
continue;
}
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
//增加对init-method、destroy-method的读取
String initMethod = bean.getAttribute("init-method");
String destroyMethodName = bean.getAttribute("destroy-method");
Class<?> clazz = Class.forName(className);
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)){
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition(clazz);
//额外设置到beanDefinition中
beanDefinition.setInitMethodName(initMethod);
beanDefinition.setDestroyMethodName(destroyMethodName);
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (!(bean.getChildNodes().item(j) instanceof Element)) {
continue;
}
if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) {
continue;
}
//解析标签:property
Element property = (Element) bean.getChildNodes().item(j);
String attrName = property.getAttribute("name");
String attrValue = property.getAttribute("value");
String attrRef = property.getAttribute("ref");
Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
PropertyValue propertyValue = new PropertyValue(attrName, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
if (getRegistry().containsBeanDefinition(beanName)) {
throw new BeansException("Duplicate beanName["+beanName+"] is not allowed");
}
getRegistry().registerBeanDefinition(beanName,beanDefinition);
}
}
}
3.实现 执行 Bean 对象的初始化方法 invokeInitMethods:
AbstractAutowireCapableBeanFactory 中:
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
// 1. 实现接口 InitializingBean
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 2. 配置信息 init-method
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
if (null == initMethod) {
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
4.定义销毁方法适配器(接口和配置):
注意原教材的问题:这里说“判断是为了避免二次执行销毁”并不准确或者说是错的,更准确的描述是:
判断的目的是为了避免同一个销毁方法被调用两次,而不是避免整个销毁动作执行两次。
public class DisposableBeanAdapter implements DisposableBean {
private final Object bean;
private final String beanName;
private String destroyMethodName;
public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {
this.bean = bean;
this.beanName = beanName;
this.destroyMethodName = beanDefinition.getDestroyMethodName();
}
@Override
public void destroy() throws Exception {
// 1. 实现接口 DisposableBean
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
// 2. 配置信息 destroy-method {判断是为了避免同一个销毁方法被调用两次,但是spring允许执行两个不同的销毁动作}
if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
if (null == destroyMethod) {
throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
destroyMethod.invoke(bean);
}
}
}
5.创建Bean时注册销毁方法对象:
在创建 Bean 对象的实例的时候,需要注册销毁方法,方便后续执行销毁动作进行调用。
注册这个方法就要有一个容器:DefaultSingletonBeanRegistry 中新增加的 Map<String, DisposableBean> disposableBeans
。这个接口的方法最终可以被类 AbstractApplicationContext 的 close 方法通过 getBeanFactory().destroySingletons()
调用。
5.1DefaultSingletonBeanRegistry 中新增销毁方法对象容器、注册方法、实现destroySingletons
方法:
注意这里逆序销毁的细节:
//注册了销毁回调的 Bean 对象的容器
private final Map<String, DisposableBean> disposableBeans = new HashMap<>();
public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
}
/**
* 销毁所有单例 Bean 对象。
*
* 该方法会遍历容器中所有已注册的实现了 DisposableBean 接口的 Bean(存储在 disposableBeans 中),
* 并按注册顺序的逆序依次调用它们的 destroy() 方法完成销毁。
*
* 逆序销毁是为了保证依赖关系正确释放(先销毁依赖者,再销毁被依赖者)。
*
* 销毁过程中如果发生异常,会包装成 BeansException 并抛出,确保调用者能感知销毁失败。
*/
public void destroySingletons() {
Set<String> keySet = this.disposableBeans.keySet();
Object[] disposableBeanNames = keySet.toArray();
//i倒序遍历:逆序销毁时因为被加入容器的顺序就暗含了依赖顺序
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
Object beanName = disposableBeanNames[i];
DisposableBean disposableBean = disposableBeans.remove(beanName);
try {
disposableBean.destroy();
} catch (Exception e) {
throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
}
}
}
5.2 在AbstractAutowireCapableBeanFactory 中注册 DisposableBean。
......
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
//创建实例
bean = createBeanInstance(beanDefinition, beanName, args);
//注入属性
applyPropertyValues(bean, beanName, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
//注册可销毁的 Bean
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
addSingleton(beanName, bean);
return bean;
}
.......
/**
* 若 Bean 实现了 DisposableBean 或配置了销毁方法,则注册其销毁适配器。
*/
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
// 使用 DisposableBeanAdapter 适配器包装 Bean
// 适配器实现了 DisposableBean 接口,
// 这样无论 Bean 是实现接口销毁,还是通过配置销毁方法,
// 都能统一以 DisposableBean 形式注册到容器,方便统一管理和调用销毁逻辑。
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
}
}
5.3 在接口 ConfigurableBeanFactory 定义了 destroySingletons 销毁方法:
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
......
/**
* 销毁单例对象
*/
void destroySingletons();
}
6.虚拟机关闭钩子注册调用销毁方法:
6.1定义接口:
在 ConfigurableApplicationContext 接口中定义注册虚拟机钩子的方法 registerShutdownHook
和手动执行关闭的方法 close
/**
* 提供了容器生命周期管理和上下文配置的扩展功能
*/
public interface ConfigurableApplicationContext extends ApplicationContext {
//刷新(启动)容器
void refresh() throws BeansException;
// 注册一个关闭钩子
void registerShutdownHook();
// 关闭容器
void close();
}
6.2:实现注册钩子和关闭容器的方法:
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
// ...
@Override
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
@Override
public void close() {
getBeanFactory().destroySingletons();
}
}
测试:
准备:
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
public void initDataMethod(){
System.out.println("执行:init-method");
hashMap.put("10001", "CMD137");
hashMap.put("10002", "CMD138");
hashMap.put("10003", "CMD139");
}
public void destroyDataMethod(){
System.out.println("执行:destroy-method");
hashMap.clear();
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
public class UserService implements InitializingBean, DisposableBean {
private String uId;
private String company;
private String location;
private UserDao userDao;
@Override
public void destroy() throws Exception {
System.out.println("执行:UserService.destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行:UserService.afterPropertiesSet");
}
// ...get/set
}
配置文件:
基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="com.miniSpring.test.bean.UserDao" init-method="initDataMethod" destroy-method="destroyDataMethod"/>
<bean id="userService" class="com.miniSpring.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
单元测试:
@Test
public void test_xml() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook();
// 2. 获取Bean对象调用方法
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}
结果:
执行:Dao的 init-method
执行:UserService.afterPropertiesSet
测试结果:CMD137,腾讯,深圳
执行:UserService.destroy
执行:Dao的 destroy-method
Process finished with exit code 0
Step8:Aware感知容器对象:
目标与必要性:
在 Spring 框架中,Aware 接口族是一种典型的“容器感知机制”(Container Awareness Mechanism),它允许 Bean 在初始化阶段主动获取并利用框架暴露的重要组件,如 BeanFactory
、ApplicationContext
、ClassLoader
等。通过这种机制,开发者不仅能够在 Bean 实例化之后接入容器的生命周期管理,还可以通过回调方式增强 Bean 的行为能力(把容器中的关键对象传递给 Bean),使其能够与 Spring 容器深度集成、协同工作。
设计:
Aware 接口本身定义为空接口,旨在作为标记性质使用;其具体功能由继承接口提供方法:
BeanFactoryAware
—— 回调获取BeanFactory
BeanClassLoaderAware
—— 回调获取ClassLoader
BeanNameAware
—— 回调获取 Bean 名称ApplicationContextAware
—— 回调获取ApplicationContext
实现机制通过 instanceof
判断和回调执行,使得 Bean 在初始化阶段能够获得所需容器资源。(在createBean()
→ initializeBean()
这条链路里完成)
特殊的,对于ApplicationContextAware
:
要将其包装为一个BeanPostProcessor (在refresh 操作时,林外还要提前注册)。ApplicationContextAwareProcessor
是一个特殊的 BeanPostProcessor
,它只负责感知并注入 ApplicationContext
。
它必须在任何 Bean 创建前就加入到 BeanFactory
的后处理链中,这样后面创建的 Bean 才能在初始化阶段感知 ApplicationContext
。
如果它等到 registerBeanPostProcessors(beanFactory)
那一步(第 5 步)才注册,就有可能漏掉在这之前创建的 Bean(例如某些基础设施 Bean 可能提前创建)。
其他 Aware 接口为什么不用提前加
BeanNameAware
、BeanFactoryAware
、EnvironmentAware
等,都是在AbstractAutowireCapableBeanFactory.initializeBean()
中通过回调直接处理的,不依赖额外的BeanPostProcessor
。- 它们都是BeanFactory 内置逻辑的一部分,而
ApplicationContextAware
的感知是ApplicationContext
层才提供的功能,所以只能通过ApplicationContextAwareProcessor
这种桥接类来做。
Spring 感知接口的设计和实现类关系:
实现:
1.定义标记接口:
/**
* 标记超接口,表明某个 Bean 有资格通过回调方法
* 被 Spring 容器通知特定的框架对象。
* 具体的方法签名由各个子接口定义,
* 通常是一个返回 void、接受单个参数的方法。
*
* 标记接口,实现该接口的 Bean 可以被 Spring 容器感知。
*/
public interface Aware {
}
通常和 instanceof 一起搭配使用。
2.容器感知类
2.1 BeanFactoryAware
/**
* 使 Bean 能感知所属的 BeanFactory 容器。
* 实现该接口的 Bean,在初始化时会被注入当前 BeanFactory。
*/
public interface BeanFactoryAware extends Aware {
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
2.2 BeanClassLoaderAware:
/**
* 使 Bean 能感知用于加载 Bean 类的 ClassLoader。
* 实现该接口的 Bean 在初始化时会被注入 ClassLoader。
*/
public interface BeanClassLoaderAware extends Aware {
void setBeanClassLoader(ClassLoader classLoader);
}
2.3 BeanNameAware:
/**
* 使 Bean 能感知其在容器中的名称。
* 实现该接口的 Bean 在初始化时会被注入 Bean 名称。
*/
public interface BeanNameAware extends Aware {
void setBeanName(String name);
}
2.4 ApplicationContextAware
/**
* 使 Bean 能感知所属的 ApplicationContext。
* 实现该接口的 Bean 在初始化时会被注入当前 ApplicationContext。
*/
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
3. 包装处理器(ApplicationContextAwareProcessor)
/**
* 负责处理实现了 ApplicationContextAware 接口的 Bean,
* 在 Bean 初始化前将当前的 ApplicationContext 注入到 Bean 中。
*/
public class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ApplicationContext applicationContext;
public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 在 Bean 初始化前调用,判断是否实现了 ApplicationContextAware,
* 若是则注入 ApplicationContext。
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(applicationContext);
}
return bean;
}
/**
* 初始化后处理,默认直接返回 Bean 不做任何处理。
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
由于 ApplicationContext 的获取并不能直接在创建 Bean 时候就可以拿到,所以需要在 refresh 操作时,把 ApplicationContext 写入到一个包装的 BeanPostProcessor 中去,再由 AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization 方法调用。
4. 注册 BeanPostProcessor
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition(从配置文件或注解中解析出 Bean 定义)
refreshBeanFactory();
// 2. 获取当前上下文使用的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 添加 ApplicationContextAwareProcessor,这是一个特殊的 BeanPostProcessor,
// 用于在 Bean 初始化前自动回调实现了 ApplicationContextAware 接口的 Bean 的 setApplicationContext 方法,
// 使这些 Bean 能够感知并持有当前 ApplicationContext 实例。
// 该处理器必须提前注册,确保后续创建的所有相关 Bean 都能正确获得 ApplicationContext。
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 4. 执行所有 BeanFactoryPostProcessor,允许修改 BeanDefinition
// (此时还未实例化任何 Bean)
invokeBeanFactoryPostProcessors(beanFactory);
// 5. 注册所有 BeanPostProcessor,为后续 Bean 实例化过程添加扩展逻辑(如 AOP、依赖注入等)
registerBeanPostProcessors(beanFactory);
// 6. 提前实例化所有非懒加载的单例 Bean(触发完整的 Bean 创建流程)
beanFactory.preInstantiateSingletons();
}
.......
}
5.感知调用操作
5.1AbstractBeanFactory 新增 用于加载 Bean 类的 ClassLoader
的管理:
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
/**
* 用于加载 Bean 类名时所使用的 ClassLoader(如果需要的话)
*/
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
//...
public ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
}
5.2 实现感知调用:
AbstractAutowireCapableBeanFactory中:
/**
* 初始化 Bean 的过程:
* 1. 调用实现了 Aware 接口的回调方法,注入相应的容器资源。
* 2. 执行所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法。
* 3. 调用 Bean 自定义的初始化方法(如 afterPropertiesSet 或配置的 init-method)。
* 4. 执行所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。
*/
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 调用 Aware 接口的回调方法,注入容器资源(具体实现由Bean自己实现)
if (bean instanceof Aware) {
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(this);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
}
// 执行 BeanPostProcessor 的初始化前置处理
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 调用 Bean 的初始化方法(afterPropertiesSet、init-method)
try {
invokeInitMethods(beanName, wrappedBean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
}
// 执行 BeanPostProcessor 的初始化后置处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
测试:
准备:
UserService新增:
public class UserService implements BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware {
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("Bean Name is:" + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("ClassLoader:" + classLoader);
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
其他都和上节一样。
单元测试:
@Test
public void test_xml() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook();
// 2. 获取Bean对象调用方法
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
System.out.println("ApplicationContextAware:"+userService.getApplicationContext());
System.out.println("BeanFactoryAware:"+userService.getBeanFactory());
}
结果:
执行:Dao的 init-method
ClassLoader:jdk.internal.loader.ClassLoaders$AppClassLoader@71bc1ae4
Bean Name is:userService
测试结果:CMD137,腾讯,深圳
ApplicationContextAware:com.miniSpring.context.support.ClassPathXmlApplicationContext@58cbafc2
BeanFactoryAware:com.miniSpring.beans.factory.support.DefaultListableBeanFactory@2034b64c
执行:Dao的 destroy-method
Process finished with exit code 0
Step 9:关于Bean对象作用域以及FactoryBean的实现和使用
在Spring框架中,Bean对象的作用域(Scope)决定了Bean实例的生命周期及其在容器中的管理方式。正确理解Bean的作用域,是合理设计应用架构、控制资源占用和提升系统性能的基础。
此外,Spring中提供了特殊接口FactoryBean
,通过实现此接口,可以自定义复杂对象的实例化逻辑,简化Bean的配置与管理,提升系统扩展性与灵活性。深入掌握FactoryBean
的实现原理及应用,有助于开发更为高效、解耦的组件。
知识点:
一、Spring Bean的作用域(Scope)
Spring容器支持多种Bean作用域,决定Bean实例的创建数量及其生命周期管理。主要作用域如下:
作用域名称 | 说明 | 生命周期 | 典型应用场景 |
---|---|---|---|
singleton | 容器中仅创建一个共享的Bean实例,默认作用域 | 容器生命周期 | 绝大多数场景,尤其无状态服务组件 |
prototype | 每次请求都会创建一个新的Bean实例 | 请求创建,容器不管理销毁 | 有状态Bean或多实例组件 |
request | 每一次HTTP请求创建一个Bean,仅在web环境有效 | HTTP请求周期 | Web应用中每请求独立对象 |
session | 每个HTTP会话创建一个Bean实例 | 会话生命周期 | Web应用中用户会话级别状态 |
application | 一个ServletContext共享一个Bean实例 | ServletContext生命周期 | Web应用全局共享组件 |
二、FactoryBean接口详解
FactoryBean
接口为Spring提供了一个强大的扩展机制。实现该接口可以自定义对象的实例化逻辑,Spring容器将通过FactoryBean
返回的对象作为Bean实例。
2.1 FactoryBean接口结构
核心方法包括:
Object getObject()
:返回由FactoryBean管理的对象实例。Class<?> getObjectType()
:返回该对象的类型。boolean isSingleton()
:指示返回的对象是否为单例。
2.2 FactoryBean的作用与优势
- 解耦实例化过程:复杂Bean的创建逻辑封装在FactoryBean中,配置更加简洁。
- 支持动态生成对象:可根据运行时条件动态返回不同实例。
- 容器管理增强:Spring容器对FactoryBean及其产物分别管理
此处有八股:
3.Bean Factory与FactoryBean有什么区别?
首先是它们的本质角色不同。Bean Factory 是 Spring IoC 容器的顶层接口,相当于 Spring 容器的核心,像我们常用的 ApplicationContext 就是它的实现类。它主要负责管理所有 Bean 的生命周期,包括注册、实例化、依赖注入这些基础操作,是 Spring 容器的基础设施。
而 FactoryBean 本质上是一个 “工厂 Bean”,它本身是 Spring 管理的一个 Bean,但特殊之处在于它能帮我们创建其他 Bean。当某个 Bean 的创建过程比较复杂,比如需要很多配置或者依赖外部资源时,就可以用 FactoryBean 来封装这些逻辑。
其次是使用场景的差异。Bean Factory 作为容器接口,更多是 Spring 内部在使用,我们开发者通常通过 ApplicationContext 来间接使用它的功能,比如调用 getBean () 方法获取 Bean。
FactoryBean 则是给开发者用的,我们可以自定义类实现这个接口,重写 getObject () 方法来定义 Bean 的创建逻辑。比如 Spring 整合 MyBatis 时用的 SqlSessionFactoryBean,就是通过它来创建 SqlSessionFactory,简化了复杂的配置过程。
最后是获取方式的不同。获取普通 Bean 直接用名称就行,但如果要获取 FactoryBean 本身,需要在名称前加个 & 符号,这也是个小细节。
简单说,Bean Factory 是管理所有 Bean 的大工厂,而 FactoryBean 是创建特定 Bean 的小工厂,两者职责和应用场景完全不同。
Spring 单例、原型以及 FactoryBean
功能实现类关系:
实现:
1.Bean的作用范围定义和xml解析:
public class BeanDefinition {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
private Class beanClass;
private PropertyValues propertyValues;
private String initMethodName;
private String destroyMethodName;
private String scope = SCOPE_SINGLETON;
private boolean singleton = true;
private boolean prototype = false;
//在xml注册Bean定义时,通过scope字段来判断是单例还是原型
public void setScope(String scope) {
this.scope = scope;
this.singleton = SCOPE_SINGLETON.equals(scope);
this.prototype = SCOPE_PROTOTYPE.equals(scope);
}
public boolean isSingleton() {
return singleton;
}
public boolean isPrototype() {
return prototype;
}
// ...get/set
}
XmlBeanDefinitionReader:
protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
for (int i = 0; i < childNodes.getLength(); i++) {
// 判断元素
if (!(childNodes.item(i) instanceof Element)) continue;
// 判断对象
if (!"bean".equals(childNodes.item(i).getNodeName())) continue;
// 解析标签
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
String initMethod = bean.getAttribute("init-method");
String destroyMethodName = bean.getAttribute("destroy-method");
String beanScope = bean.getAttribute("scope");
// 获取 Class,方便获取类中的名称
Class<?> clazz = Class.forName(className);
// 优先级 id > name
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
// 定义Bean
BeanDefinition beanDefinition = new BeanDefinition(clazz);
beanDefinition.setInitMethodName(initMethod);
beanDefinition.setDestroyMethodName(destroyMethodName);
//设置Scope字段:
if (StrUtil.isNotEmpty(beanScope)) {
beanDefinition.setScope(beanScope);
}
// ...
// 注册 BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
2.创建和修改对象时候判断单例和原型模式
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
// 注册实现了 DisposableBean 接口的 Bean 对象
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
// 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE
if (beanDefinition.isSingleton()) {
addSingleton(beanName, bean);
}
return bean;
}
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
// 非 Singleton 类型的 Bean 不执行销毁方法
if (!beanDefinition.isSingleton()) return;
if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
}
}
// ... 其他功能
}
单例模式和原型模式的区别就在于是否存放到内存中,如果是原型模式那么就不会存放到内存中,每次获取都重新创建对象,另外非 Singleton 类型的 Bean 不需要执行销毁方法。
3.定义 FactoryBean 接口:
/**
* FactoryBean 是 Spring 提供的一种特殊 Bean 接口,
* 用于自定义复杂对象的创建逻辑。
*
* 它的作用是:当一个 Bean 实现了 FactoryBean 接口,
* Spring 容器在获取该 Bean 时,返回的是 getObject() 方法
* 生产的对象(称为“产物对象”),而不是 FactoryBean 本身。
* 如果需要获取工厂对象本身,可以在 getBean 时在名称前加 "&" 前缀。
*/
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
4.实现一个 FactoryBean 注册服务:
/**
* FactoryBeanRegistrySupport
*
* 该类是 Spring 容器中用于支持 FactoryBean 产物对象(getObject() 返回值)缓存和获取的抽象基类,
* 继承自 DefaultSingletonBeanRegistry,因此具备单例对象注册与缓存的能力。
*
* 核心职责:
* 1. 缓存由 FactoryBean 创建的单例产物对象(与工厂对象本身分开管理)
* 2. 根据 FactoryBean 的 singleton/prototype 特性,决定是否使用缓存
* 3. 统一处理 FactoryBean 产物对象的创建异常
*
* 设计背景:
* - 在 Spring 中,FactoryBean 本身也是一个 Bean,但我们更多关心它产出的对象(getObject())。
* - 如果 FactoryBean 的产物是单例,需要在容器中缓存;如果是原型,每次都重新调用 getObject()。
* - 此类提供了一个专门的缓存 Map(factoryBeanObjectCache)来管理这些产物对象,避免与普通 Bean 缓存混淆。
*/
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
/**
* FactoryBean 产物对象的单例缓存:
* key = FactoryBean 的 beanName
* value = FactoryBean 创建的对象实例(getObject() 的返回值)
*
* 注意:
* - 这里缓存的是产物对象,而不是 FactoryBean 本身
* - 如果 getObject() 返回 null,会使用 NULL_OBJECT 占位符避免重复调用
*/
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
/**
* 从 FactoryBean 产物缓存中获取对象(不触发创建)。
*/
protected Object getCachedObjectForFactoryBean(String beanName) {
Object object = this.factoryBeanObjectCache.get(beanName);
return (object != NULL_OBJECT ? object : null);
}
/**
* 根据 FactoryBean 获取对象,带有缓存逻辑。
* - 如果 FactoryBean 是单例:
* 1. 先从缓存获取
* 2. 缓存没有则调用 doGetObjectFromFactoryBean 创建,并放入缓存
* - 如果是原型:
* 每次都调用 doGetObjectFromFactoryBean 创建新对象,不缓存
*/
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {
if (factory.isSingleton()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = doGetObjectFromFactoryBean(factory, beanName);
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
}
return (object != NULL_OBJECT ? object : null);
} else {
return doGetObjectFromFactoryBean(factory, beanName);
}
}
/**
* 真正调用 FactoryBean.getObject() 创建产物对象的方法。
* 对异常进行统一封装为 BeansException。
*/
private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName) {
try {
return factory.getObject();
} catch (Exception e) {
throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e);
}
}
}
5.扩展 AbstractBeanFactory 创建对象逻辑:
- 首先这里把 AbstractBeanFactory 原来继承的 DefaultSingletonBeanRegistry,修改为继承 FactoryBeanRegistrySupport。因为需要扩展出创建 FactoryBean 对象的能力,所以这就想一个链条服务上,截出一个段来处理额外的服务,并把链条再链接上。
- 此处新增加的功能主要是在 doGetBean 方法中,添加了调用
(T) getObjectForBeanInstance(sharedInstance, name)
对获取 FactoryBean 的操作。 - 在 getObjectForBeanInstance 方法中做具体的 instanceof 判断,另外还会从 FactoryBean 的缓存中获取对象,如果不存在则调用 FactoryBeanRegistrySupport#getObjectFromFactoryBean,执行具体的操作。
protected <T> T doGetBean(final String name, final Object[] args) {
Object sharedInstance = getSingleton(name);
if (sharedInstance != null) {
// 如果是 FactoryBean,则需要调用 FactoryBean#getObject
return (T) getObjectForBeanInstance(sharedInstance, name);
}
BeanDefinition beanDefinition = getBeanDefinition(name);
Object bean = createBean(name, beanDefinition, args);
return (T) getObjectForBeanInstance(bean, name);
}
private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
// 普通 Bean,直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
// FactoryBean 产物缓存中获取已有对象
Object object = getCachedObjectForFactoryBean(beanName);
// 缓存中没有,则调用 FactoryBean 创建产物
if (object == null) {
FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
object = getObjectFromFactoryBean(factoryBean, beanName);
}
return object;
}
测试:
准备:
IUserDao:
这个章节我们删掉 UserDao,定义一个 IUserDao 接口,之所这样做是为了通过 FactoryBean 做一个自定义对象的代理操作。
public interface IUserDao {
String queryUserName(String uId);
}
定义 FactoryBean 对象:
/**
* ProxyBeanFactory 实现了 FactoryBean 接口,用于创建 IUserDao 接口的动态代理实例。
*
* 通过动态代理机制,代理 IUserDao 接口的调用,拦截方法调用并返回自定义逻辑结果。
*
* 该工厂类演示了如何利用 JDK 动态代理结合 FactoryBean 来生成接口的代理对象,
* 实现了接口方法的增强和行为定制。
*/
public class ProxyBeanFactory implements FactoryBean<IUserDao> {
/**
* 通过 JDK 动态代理创建 IUserDao 接口的代理实例。
* 代理对象拦截所有方法调用,利用 InvocationHandler 实现方法增强:
* - 根据方法入参,从模拟的 Map 中查询对应的用户名。
* - 拼接返回字符串,示意该方法被代理拦截。
*
* 关键点说明:
* - Proxy.newProxyInstance 创建动态代理对象。
* - 第一个参数为类加载器,通常使用当前线程上下文类加载器。
* - 第二个参数是代理接口列表,这里代理 IUserDao。
* - 第三个参数是 InvocationHandler,实现方法调用的拦截逻辑。
*
* @return IUserDao 接口的代理对象
* @throws Exception 创建代理对象时可能抛出的异常
*/
@Override
public IUserDao getObject() throws Exception {
InvocationHandler handler = (proxy, method, args) -> {
// 模拟数据库或数据源
Map<String, String> hashMap = new HashMap<>();
hashMap.put("10001", "CMD137");
hashMap.put("10002", "CMD138");
hashMap.put("10003", "CMD139");
// 返回动态代理的结果,说明调用了代理方法
return "你被代理了 " + method.getName() + ":" + hashMap.get(args[0].toString());
};
// 创建并返回代理实例,实现 IUserDao 接口
return (IUserDao) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{IUserDao.class},
handler
);
}
/**
* 返回该 FactoryBean 创建对象的类型,即 IUserDao 接口类型。
* 容器据此进行类型推断和自动装配。
*
* @return IUserDao 的 Class 对象
*/
@Override
public Class<?> getObjectType() {
return IUserDao.class;
}
/**
* 指示该 FactoryBean 创建的对象是否为单例。
* 返回 true,表示代理对象在容器中是单例,缓存共享。
*
* @return true,单例对象
*/
@Override
public boolean isSingleton() {
return true;
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userService" class="com.miniSpring.test.bean.UserService" scope="prototype">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="proxyUserDao"/>
</bean>
<bean id="proxyUserDao" class="com.miniSpring.test.bean.ProxyBeanFactory"/>
</beans>
单元测试(单例&原型):
@Test
public void test_prototype() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook();
// 2. 获取Bean对象调用方法
UserService userService01 = applicationContext.getBean("userService", UserService.class);
UserService userService02 = applicationContext.getBean("userService", UserService.class);
// 3. 配置 scope="prototype/singleton"
System.out.println("userService01:"+userService01);
System.out.println("userService02:"+userService02);
// 4.检验userService原型模式(应为false)
System.out.println("userService01 == userService02?");
System.out.println(userService01 == userService02);
// 5. 检验userDao单例模式(应为true)
System.out.println("userService01.getUserDao() == userService02.getUserDao()?");
System.out.println(userService01.getUserDao() == userService02.getUserDao());
}
结果:
userService01:com.miniSpring.test.bean.UserService$$EnhancerByCGLIB$$593a1b41@54422e18
userService02:com.miniSpring.test.bean.UserService$$EnhancerByCGLIB$$593a1b41@117159c0
userService01 == userService02?
false
userService01.getUserDao() == userService02.getUserDao()?
true
可以看到两个userService的hashcode不一样,说明原型模式配置生效;
而两个userService的UserDao()比较是相同的,说明代理的单例模式生效。
单元测试(代理对象):
@Test
public void test_factory_bean() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook();
// 2. 调用代理方法
UserService userService = applicationContext.getBean("userService", UserService.class);
System.out.println("测试结果:" + userService.queryUserInfo());
}
测试结果:
测试结果:你被代理了 queryUserName:CMD137,腾讯,深圳
代理生效。
Step 10:容器事件和事件监听器
在 Spring 框架中,容器不仅负责管理 Bean 的生命周期,还提供了事件发布与监听机制,用于在应用运行过程中实现松耦合的组件间通信。
这种机制基于观察者模式(Observer Pattern),核心由三部分组成:
- 事件(ApplicationEvent)
用于描述在容器中发生的特定动作,例如容器启动、刷新、关闭等,也可以是用户自定义的业务事件。 - 事件发布器(ApplicationEventPublisher)
容器自身充当事件发布器,当特定动作发生时,会将事件对象传递给所有匹配的监听器。 - 事件监听器(ApplicationListener)
实现该接口的类可接收并处理特定类型的事件,从而在事件发生时自动触发相应逻辑。
这种机制在实际项目中广泛用于:
- 容器生命周期回调(如启动、关闭时执行资源初始化或释放)。
- 组件解耦(不同模块通过事件异步通信,而无需直接依赖)。
- 业务逻辑扩展(可在不修改核心流程的前提下,插入新功能)。
设计:
在功能实现上我们需要定义出事件类、事件监听、事件发布,而这些类的功能需要结合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件监听器的操作。整体设计结构如下图:
容器事件和事件监听器实现类关系:
实现:
0.拓展工具类:
ClassUtils:
/**
* 判断指定类是否为 CGLIB 动态代理生成的类。
*
* @param clazz 要检查的类对象
* @return 如果是 CGLIB 代理类返回 true,否则返回 false
*/
public static boolean isCglibProxyClass(Class<?> clazz) {
return (clazz != null && isCglibProxyClassName(clazz.getName()));
}
/**
* 判断指定类名是否为 CGLIB 动态代理生成的类名。
*
* @param className 要检查的类名字符串
* @return 如果类名包含 CGLIB 代理标识“$$”返回 true,否则返回 false
*/
public static boolean isCglibProxyClassName(String className) {
return (className != null && className.contains("$$"));
}
1.定义和实现事件:
- ApplicationEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。
- ContextClosedEvent、ContextRefreshedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于监听刷新和关闭动作。
ApplicationEvent:
/**
* 具备事件功能的抽象类
* 继承自 {@link java.util.EventObject},封装了事件源对象。
* 所有容器事件与自定义事件都应继承该类。
*/
public abstract class ApplicationEvent extends EventObject {
/**
* 构造事件对象。
*/
public ApplicationEvent(Object source) {
super(source);
}
}
ApplicationContextEvent:
/**
* 应用上下文事件基类。
* 继承自 {@link ApplicationEvent},用于封装与 {@link ApplicationContext} 相关的事件。
* 所有容器级别的事件(如刷新、启动、关闭)都应继承该类。
*/
public class ApplicationContextEvent extends ApplicationEvent {
/**
* 创建应用上下文事件。
*/
public ApplicationContextEvent(Object source) {
super(source);
}
/**
* 获取触发该事件的 {@link ApplicationContext}。
*/
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
ContextClosedEvent:
/**
* 容器关闭事件。
* 当 {@link ApplicationContext} 被关闭时发布。
*/
public class ContextClosedEvent extends ApplicationContextEvent {
/**
* 创建容器关闭事件。
*
* @param source 事件源,即被关闭的 {@link ApplicationContext}。
*/
public ContextClosedEvent(Object source) {
super(source);
}
}
ContextRefreshedEvent:
/**
* 容器刷新事件。
* 当 {@link ApplicationContext} 初始化或刷新完成时发布。
*/
public class ContextRefreshedEvent extends ApplicationContextEvent {
/**
* 创建容器刷新事件。
*
* @param source 事件源,即已刷新完成的 {@link ApplicationContext}。
*/
public ContextRefreshedEvent(Object source) {
super(source);
}
}
2.事件监听器:
/**
* 应用事件监听器接口,需由事件监听器实现。
* * 基于标准的 <code>java.util.EventListener</code> 接口,
* * 采用观察者设计模式(Observer Pattern)。
*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 处理应用事件
*/
void onApplicationEvent(E event);
}
3.事件广播器:
接收事件发布器传来的事件,然后遍历所有已注册监听器,调用它们的事件处理方法,实现事件的分发。
在 Spring 框架中,ApplicationEventMulticaster
是事件广播器的核心接口。它由容器维护,所有监听器都向它注册,当事件发生时,它会调用相应监听器的回调方法。
3.1 应用事件广播器接口:
/**
* 应用事件广播器接口。
* 负责将 {@link ApplicationEvent} 广播给所有已注册的 {@link ApplicationListener}。
* 广播器不关心事件的具体来源,只负责分发到合适的监听器。
*/
public interface ApplicationEventMulticaster {
/**
* 添加事件监听器,使其能接收所有事件通知。
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 移除事件监听器,使其不再接收事件通知。
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* 将事件广播给所有匹配的监听器。
*/
void multicastEvent(ApplicationEvent event);
}
3.2 抽象应用事件广播器基类:
这个类是事件广播器的抽象基础,实现了事件监听器的管理和筛选功能。它维护了所有注册的事件监听器,并能根据发布的事件类型,筛选出对该事件感兴趣的监听器,确保事件只被合适的监听器处理。通过泛型反射判断监听器支持的事件类型,支持事件继承体系。
implements BeanFactoryAware可以让 AbstractApplicationEventMulticaster
能够获取到 Spring 容器中的 BeanFactory
实例,从而方便它在事件广播过程中对监听器或相关 Bean 进行依赖注入、延迟初始化或获取其他容器管理的资源。
/**
* 抽象应用事件广播器基类,实现了 ApplicationEventMulticaster 和 BeanFactoryAware。
* 维护注册的事件监听器集合,负责筛选合适监听器以分发事件。
* 支持基于泛型参数类型判断监听器是否对特定事件感兴趣。
*/
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
/**
* 已注册的事件监听器集合,使用 LinkedHashSet 保证顺序且去重。
*/
public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
/**
* Spring 容器 BeanFactory 引用,用于监听器或事件的依赖注入。
*/
private BeanFactory beanFactory;
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
applicationListeners.remove(listener);
}
@Override
public final void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* 根据事件类型筛选出所有感兴趣的监听器。
*
* @param event 当前发布的事件
* @return 支持该事件的监听器集合
*/
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
LinkedList<ApplicationListener> allListeners = new LinkedList<>();
// 遍历已注册监听器,判断是否支持该事件
for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
if (supportsEvent(listener, event)) {
allListeners.add(listener);
}
}
return allListeners;
}
/**
* 判断监听器是否支持处理该事件。
* 通过反射获取监听器泛型参数的实际类型,判断该类型是否为事件对象的超类或接口。
*
* @param applicationListener 事件监听器
* @param event 事件对象
* @return 如果监听器泛型类型能接收该事件,返回 true;否则 false。
*/
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
// 获取监听器实现类
Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
// 判断是否为 CGLIB 代理类,是则获取其父类(代理类本身不包含泛型信息,需要获取其父类(即被代理的目标类)来准确获取泛型参数)
Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
// 获取实现的第一个接口的泛型类型(通常为 ApplicationListener<E>)
Type genericInterface = targetClass.getGenericInterfaces()[0];
// 获取泛型参数 E 的实际类型
Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
String className = actualTypeArgument.getTypeName();
Class<?> eventClassName;
try {
// 加载泛型参数对应的事件类
eventClassName = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("错误的事件类名: " + className);
}
// 判断事件类是否为泛型参数类或其子类(支持事件继承)
return eventClassName.isAssignableFrom(event.getClass());
}
}
3.3 简单的应用事件广播器实现类:
- – 管理事件监听器的注册和筛选
- – 实现事件的同步发布和通知机制
/**
* 简单的应用事件广播器实现类,继承自 AbstractApplicationEventMulticaster。
* 负责将事件同步地广播给所有匹配的监听器。
* 通过构造方法注入 BeanFactory,实现监听器的依赖管理。
*
* 作用:
* - 管理事件监听器的注册和筛选
* - 实现事件的同步发布和通知机制
*/
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
}
@SuppressWarnings("unchecked")
@Override
public void multicastEvent(final ApplicationEvent event) {
// 遍历所有支持该事件的监听器,逐个同步调用其事件处理方法
for (final ApplicationListener listener : getApplicationListeners(event)) {
listener.onApplicationEvent(event);
}
}
}
4.事件发布者的定义和实现:
4.1 registerSingleton:
这里又要回到SingletonBeanRegistry补充注册单例对象到容器方法:
/**
* 注册单例对象到容器,名称唯一。
*/
void registerSingleton(String beanName, Object singletonObject);
容器的单例管理机制,事件广播器作为关键组件,需要被容器以单例方式管理和共享,调用该方法完成注册。这是 Spring 容器内部设计的标准操作,保证了广播器的生命周期和访问一致性。是把已有的对象(此处是事件广播器实例)直接注册为单例 Bean,区别于通过配置或注解创建 Bean。这是一种“程序式”注册单例的方式。
那么实现该接口的类就要实现:
DefaultSingletonBeanRegistry
public void registerSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}
4.2 在AbstractApplicationContext 中添加相关机制:
先让 ApplicationContext 继承:
extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader, ApplicationEventPublisher。
东一下西一下堪比之前的操作系统课设,后悔看这个教程了,基本全靠GPT讲解,原教材只是在别人写的上面加了一堆莫名其妙的引言,架构图也不完整,服了……………………
ApplicationEventPublisher 是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
private ApplicationEventMulticaster applicationEventMulticaster;
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition(从配置文件或注解中解析出 Bean 定义)
refreshBeanFactory();
// 2. 获取当前上下文使用的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 添加 ApplicationContextAwareProcessor,这是一个特殊的 BeanPostProcessor,
// 用于在 Bean 初始化前自动回调实现了 ApplicationContextAware 接口的 Bean 的 setApplicationContext 方法,
// 使这些 Bean 能够感知并持有当前 ApplicationContext 实例。
// 该处理器必须提前注册,确保后续创建的所有相关 Bean 都能正确获得 ApplicationContext。
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 4. 执行所有 BeanFactoryPostProcessor,允许修改 BeanDefinition
// (此时还未实例化任何 Bean)
invokeBeanFactoryPostProcessors(beanFactory);
// 5. 注册所有 BeanPostProcessor,为后续 Bean 实例化过程添加扩展逻辑(如 AOP、依赖注入等)
registerBeanPostProcessors(beanFactory);
// 6. 初始化事件发布者
initApplicationEventMulticaster();
// 7. 注册事件监听器
registerListeners();
// 8. 提前实例化所有非懒加载的单例 Bean(触发完整的 Bean 创建流程)
beanFactory.preInstantiateSingletons();
// 9. 发布容器刷新完成事件
finishRefresh();
}
/**
* 初始化事件广播器。
* 从 BeanFactory 获取或创建一个 SimpleApplicationEventMulticaster 实例,
* 并将其注册为单例 Bean,供容器管理和事件发布使用。
*/
private void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
}
/**
* 注册所有已定义的事件监听器。
* 从容器中获取所有实现了 ApplicationListener 接口的 Bean,
* 并将它们添加到事件广播器的监听器集合中。
*/
private void registerListeners() {
Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
for (ApplicationListener listener : applicationListeners) {
applicationEventMulticaster.addApplicationListener(listener);
}
}
/**
* 完成容器刷新操作后的收尾工作。
* 发布一个 ContextRefreshedEvent 事件,通知所有监听器容器已刷新完成。
*/
private void finishRefresh() {
publishEvent(new ContextRefreshedEvent(this));
}
/**
* 发布事件接口实现。
* 将事件交由事件广播器进行多播,通知所有匹配的监听器。
*
* @param event 要发布的应用事件
*/
@Override
public void publishEvent(ApplicationEvent event) {
applicationEventMulticaster.multicastEvent(event);
}
@Override
public void close() {
// 发布容器关闭事件
publishEvent(new ContextClosedEvent(this));
// 执行销毁单例bean的销毁方法
getBeanFactory().destroySingletons();
}
}
- 在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了
初始化事件发布者
、注册事件监听器
、发布容器刷新完成事件
,三个方法用于处理事件操作。 - 初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件广播器。
- 注册事件监听器(registerListeners),通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。
- 发布容器刷新完成事件(finishRefresh),发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 方法。
- 最后是一个 close 方法中,新增加了发布一个容器关闭事件。
publishEvent(new ContextClosedEvent(this));
测试:
1.创建一个事件和监听器:
public class CustomEvent extends ApplicationContextEvent {
private Long id;
private String message;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public CustomEvent(Object source, Long id, String message) {
super(source);
this.id = id;
this.message = message;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class CustomEventListener_1 implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("CustomEventListener_1监听到事件:" + event);
System.out.println("\t收到:" + event.getSource() + "消息;时间:" + new Date());
System.out.println("\t消息:" + event.getId() + ":" + event.getMessage());
}
}
public class CustomEventListener_2 implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("CustomEventListener_2监听到事件:" + event);
System.out.println("\t处理Custom业务中......");
}
}
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("刷新事件:" + this.getClass().getName());
}
}
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("关闭事件:" + this.getClass().getName());
}
}
2. 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean class="cn.bugstack.springframework.test.event.ContextRefreshedEventListener"/>
<bean class="cn.bugstack.springframework.test.event.CustomEventListener"/>
<bean class="cn.bugstack.springframework.test.event.ContextClosedEventListener"/>
</beans>
3. 单元测试:
public class ApiTest {
@Test
public void test_event() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));
applicationContext.registerShutdownHook();
}
}
结果:
刷新事件:com.miniSpring.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$f5717a18
CustomEventListener_1监听到事件:com.miniSpring.test.event.CustomEvent[source=com.miniSpring.context.support.ClassPathXmlApplicationContext@644baf4a]
收到:com.miniSpring.context.support.ClassPathXmlApplicationContext@644baf4a消息;时间:Sun Aug 10 17:36:32 CST 2025
消息:1019129009086763:成功了!
CustomEventListener_2监听到事件:com.miniSpring.test.event.CustomEvent[source=com.miniSpring.context.support.ClassPathXmlApplicationContext@644baf4a]
处理Custom业务中......
关闭事件:com.miniSpring.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$fe9f94a
容器事件与事件监听机制完整调用链详解:
关键组件
- ApplicationEvent:事件对象,描述容器发生的某种动作(如刷新、关闭),也支持用户自定义事件。
- ApplicationListener:事件监听器接口,实现此接口的类可以接收并处理指定类型的事件。
- ApplicationEventMulticaster(事件广播器):负责维护监听器列表,并将事件广播给匹配的监听器。
- ApplicationEventPublisher:事件发布接口,发布事件的入口。
- ApplicationContext:容器核心接口,集成事件发布器,管理 Bean、监听器及事件的生命周期。
1. 容器启动阶段
- 容器初始化时,调用
initApplicationEventMulticaster()
:- 创建一个
SimpleApplicationEventMulticaster
实例(实现了事件广播器功能)。 - 通过
beanFactory.registerSingleton
将该广播器注册为单例 Bean,供容器管理。
- 创建一个
- 调用
registerListeners()
:- 容器扫描并获取所有实现了
ApplicationListener
接口的监听器 Bean。 - 将这些监听器注册到
applicationEventMulticaster
中,保存到监听器集合里。
- 容器扫描并获取所有实现了
2. 事件发布流程
- 发布事件:容器调用
publishEvent(ApplicationEvent event)
方法。 - 事件发布方法内部,调用
applicationEventMulticaster.multicastEvent(event)
,触发事件广播。
3. 事件广播过程
SimpleApplicationEventMulticaster.multicastEvent(event)
:- 调用基类
AbstractApplicationEventMulticaster.getApplicationListeners(event)
获取对该事件感兴趣的所有监听器。
- 调用基类
AbstractApplicationEventMulticaster.getApplicationListeners(event)
:- 遍历已注册监听器集合。
- 通过
supportsEvent(listener, event)
判断监听器是否支持该事件类型。- 利用反射获取监听器泛型参数(监听的事件类型)。
- 判断该泛型事件类型是否为发布事件的父类或接口(支持事件继承)。
- 特殊处理:如果监听器是 CGLIB 动态代理类,则获取其父类进行泛型类型解析,保证正确性。
- 返回所有匹配监听器列表。
SimpleApplicationEventMulticaster
按顺序逐个调用监听器的onApplicationEvent(event)
方法,通知监听器处理事件。
4. 监听器响应事件
- 每个实现了
ApplicationListener
的类会执行自己的onApplicationEvent
方法,完成自定义业务逻辑。
5. 典型事件示例
- 当容器刷新完成时,容器调用
finishRefresh()
:- 触发
publishEvent(new ContextRefreshedEvent(this))
。 - 事件广播器将该刷新事件推送给所有感兴趣的监听器。
- 触发
总结图示(调用链)
scss复制编辑容器启动
↓
initApplicationEventMulticaster() 创建并注册事件广播器
↓
registerListeners() 注册所有监听器到广播器
↓
publishEvent(event) 调用事件发布接口
↓
applicationEventMulticaster.multicastEvent(event) 事件广播
↓
getApplicationListeners(event) 筛选支持事件的监听器
↓
supportsEvent() 判断监听器泛型是否支持该事件类型
↓
遍历支持事件的监听器列表
↓
调用 listener.onApplicationEvent(event)
↓
监听器执行自定义事件处理逻辑
机制核心要点
- 事件和监听器解耦:事件发布者只负责发布事件,监听器独立处理,互不依赖。
- 泛型类型匹配:支持监听器只对特定事件类型感兴趣,避免无关监听器收到事件。
- 代理类兼容:对 CGLIB 代理类特殊处理,保证泛型判断正确。
- 单例广播器:广播器作为容器单例,统一管理事件分发,保证一致性。
- 同步广播:事件同步通知监听器,简化流程。