Spring-IOC
SpringIOC
创建简单的 Bean 容器#
凡是可以存放数据的具体数据结构实现,都可以称之为容器。例如:ArrayList、LinkedList、HashSet 等,但在 Spring Bean 容器的场景下,我们需要一种可以用于存放和名称索引式的数据结构,所以选择 HashMap 是最合适不过的。
一个简单的 Spring Bean 容器实现,还需 Bean 的定义、注册、获取三个基本步骤:
- 定义:BeanDefinition,可能这是你在查阅 Spring 源码时经常看到的一个类,例如它会包括 singleton、prototype、BeanClassName 等。但目前我们初步实现会更加简单的处理,只定义一个 Object 类型用于存放对象。
- 注册:这个过程就是我们把对象存放到 HashMap 中,也就是存放到容器中。
- 获取:最后就是获取对象,Bean 的名字就是 key。Spring 容器初始化好 Bean 以后,就可以直接获取了。
实现 Bean 的定义、注册、获取#
先放张类的继承与实现关系图:

项目目录结构:

BeanDefinition.class
public class BeanDefinition {
private Class beanClass;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
}
BeanDefinition 将 Bean 的定义信息(如类名、作用域、构造函数参数等)与 Bean 的实例分离开来。
这意味着在 Spring 容器启动时,可以先解析配置文件(如 XML 或注解),将其中的 Bean 定义信息封装成 BeanDefinition
对象,而无需立即创建 Bean 实例。直到真正需要使用这些 Bean 时,再根据 BeanDefinition
中的信息进行实例化。
DefaultListableBeanFactory.class
- DefaultListableBeanFactory 在 Spring 源码中也是一个非常核心的类,在我们目前的实现中也是逐步贴近于源码,与源码类名保持一致。
- DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactory 和 AbstractBeanFactory 等一连串的功能实现。所以有时候你会看到一些类的强转,调用某些方法,也是因为你强转的类实现接口或继承了某些类。
- 除此之外这个类还实现了接口 BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法,当然你还会看到一个 getBeanDefinition 的实现,这个方法我们文中提到过它是抽象类 AbstractBeanFactory 中定义的抽象方法。现在注册 Bean 定义与获取 Bean 定义就可以同时使用了,是不感觉这个套路还蛮深的。接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的 beanDefinitionMap 里
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {
private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) throw new BeansException("No bean named '" + beanName + "' is defined");
return beanDefinition;
}
}
DefaultListableBeanFactory 类的功能是对传入的 BeanDefinition 对象进行注册和获取。
registerBeanDefinition
将 BeanDefinition 存储到容器中。(注意,传入的 BeanDefinition 对象中有个 beanClass 属性,代表传入的 bean 类,比如 UserService 类,因此存储到容器中的也仅仅只是一个 未实例化
的类)
getBeanDefinition
将容器中的 BeanDefinition 对象取出来。(这就对应前面讲的:将其中的 Bean 定义信息封装成 BeanDefinition 对象,需要的时候通过 getBeanDefinition 获取到 BeanDefinition 对象,进而获取到 beanClass 类,再对 beanClass 类进行实例化。)
从上面的解释可以很清楚的了解到:DefaultListableBeanFactory 的作用就是注册和获取 beanClass。
我们写个用例测试一下:
@Test
public void test_xml() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2.注册bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 3.获取 bean
System.out.println(beanFactory.getBeanDefinition("userService").getBeanClass());
}

从运行结果来看:实际上存入的并不是实例化后的对象,实际上只是一个类。
AbstractBeanFactory.class
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
@Override
public Object getBean(String name) throws BeansException {
Object bean = getSingleton(name);
if (bean != null) {
return bean;
}
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
}
AbstractBeanFactory 类定义了获取类的 实例化
的方法。现在 单例Bean
容器中获取类的实例化对象,若没有,则通过方法 createBean
对类进行实例化。并将实例化后的对象放入 单例Bean容器
中,供后续使用。
createBean 的具体实现是在 AbstractAutowireCapableBeanFactory
中。
AbstractAutowireCapableBeanFactory.class
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
Object bean;
try {
bean = beanDefinition.getBeanClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
}
AbstractAutowireCapableBeanFactory 类中的 createBean 方法定义了一个实例化 Bean 对象的流程。
先通过 beanDefinition.getBeanClass() 获取到 bean 的类。(前面我们说过,将实体类作为参数,创建 BeanDefinition 时,bean 类是存放在 BeanDefinition 对象的属性 beanClass 中的)。然后通过 newInstance() 方法创建 Bean 实例。最后将创建的 bean 实例添加到单例 Bean 容器中。
DefaultSingletonBeanRegistry.class
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
private final Map<String, Object> singletonObjects = new HashMap<>();
@Override
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}
protected void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}
}
在 DefaultSingletonBeanRegistry 中主要实现 getSingleton 方法,同时实现了一个受保护的 addSingleton 方法,这个方法可以被继承此类的其他类调用。包括:AbstractBeanFactory 以及继承的 DefaultListableBeanFactory 调用。
总结:可以将这些类和接口分为三个部分
- BeanDefinitionRegistry 和 DefaultListableBeanFactory。这部分主要是实现了对 BeanDefinition 对象的注册和获取,但是并不对类进行实例化。
- BeanFactory、AbstractBeanFactory 和 AbstractAutowireCapableBeanFactory。这部分主要是实现了实例化 Bean 对象的功能,并将实例化后的 Bean 对象放到单例容器中。
- SingletonBeanRegistry 和 DefaultSingletonBeanRegistry。这部分的功能就是实现对实例化后的单例 Bean 对象的获取。同时实现了一个受保护的 addSingleton 方法,这个方法可以被继承此类的其他类调用。
测试:
@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
UserService userService_singleton = (UserService) beanFactory.getSingleton("userService");
userService_singleton.queryUserInfo();
}
上面实现的简单的注册和获取 Bean 流程,结果如下:

在第一次获取 Bean 时,单例 Bean 容器中并没有实例化的 Bean 对象,但是在第一次实例化 Bean 对象后,就会将实例化的 Bean 对象放到单例容器中,因此在第二次获取 Bean 时,可以直接调用 DefaultSingletonBeanRegistry 类中的 getSingleton 方法获取 Bean。
基于 CGLIB 实现含构造函数的类实例化策略#
在上一节中,实现了将实例化对象交给容器来统一管理,我们在注入 Bean 时,只需传入一个 UserService.class,具体的实例化过程由容器来实现并管理。但是如果实例化的对象是一个含有构造函数的对象,就会报错。
UserService.class
public class UserService {
private String name;
public UserService(String name){
this.name = name;
}
public void queryUserInfo(){
System.out.println("查询用户信息");
}
}
测试用例:
@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
UserService userService_singleton = (UserService) beanFactory.getSingleton("userService");
userService_singleton.queryUserInfo();
}
报错:

发生这一现象的主要原因就是因为 beanDefinition.getBeanClass().newInstance();
实例化方式并没有考虑构造函数的入参.
因此,这一节的目标就是加入对有构造函数的对象的实例化。
- 使用 Java 自带的方法 DeclaredConstructor
- 使用 CGLIB 动态创建 Bean 对象
代码结构#

新增 getBean 接口#
有一个需要注意的点是,我们对对象的实例化是在调用 getBean 方法是完成的,也就是说只有我们需要用到的时侯,才会去实例化 Bean 对象。
之前的 BeanFactory 中有一个 getBean 方法,这个我们重载一个 getBean 方法,加入构造函数的参数。
public interface BeanFactory {
Object getBean(String name) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
}
定义实例化策略接口#
这里定义一个实例化策略的接口,规范实例化策略方法的参数等信息。
beanDefinition、beanName 都明白含义和作用。主要讲 Constructor ctor 和 Object [] args。
- Constructor 包含了类的一些必要信息,加入这个参数就是为了拿到符合入参信息相对于的构造函数
- args 就比较容易理解,就是传进来的构造函数参数值
public interface InstantiationStrategy {
Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;
}
Constructor 实例化#
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 clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
} else {
return clazz.getDeclaredConstructor().newInstance();
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
}
}
}
这个方法重写了实例化策略接口中的类。
逻辑就是根据传递进来的构造函数判断。
-
如果传进来的构造函数 ctor 为 null,说明需要实例化的是
无参构造函数
。用
clazz.getDeclaredConstructor().newInstance();
实例化 -
如果 ctor 不为 null,说明需要实例化的是
带参构造函数
。用
clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
实例化
CGLIB 实例化#
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
if (null == ctor) return enhancer.create();
return enhancer.create(ctor.getParameterTypes(), args);
}
}
AbstractBeanFactory.class#
上面提到过,对象的实例化是在调用 getBean 方法是完成的,我们在 BeanFactory 中也重载了 getBean 方法,因此在 AbstractBeanFactory 类中重写带构造函数参数的 getBean 方法。
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;
}
AbstractAutowireCapableBeanFactory.class#
最重要的就是对 AbstractAutowireCapableBeanFactory 类的修改。
- 首先在类中定义了一个实例化策略对象
InstantiationStrategy instantiationStrategy
,这里选择 SimpleInstantiationStrategy,也可以改成 CglibSubclassingInstantiationStrategy - 之后抽取
createBeanInstance
方法,该方法是根据传入的构造函数参数 args,判断需要实例化的是哪个构造函数的对象 - 最后就是
getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
调用对应实例化策略的实例化方法,实例化 Bean 对象。
但是需要注意的是,这里仅仅按照参数数量来选择构造函数,没有考虑参数类型的匹配。后面测试时会验证这个错误点。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();
@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();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
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;
}
}
测试#
这里调用 getBean 方法不仅传入 Bean 对象名称,还传入构造函数参数。
@Test
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 3. 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 4.获取bean
UserService userService = (UserService) beanFactory.getBean("userService", "LCK");
userService.queryUserInfo();
}

问题#
但是我们上面提到,在实例化时时根据参数数量来选择构造函数的,实际上也应该考虑参数类型的匹配。
这里我们调用 getBean 时,多传入一个参数 23
试试:
@Test
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 3. 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 4.获取bean
UserService userService = (UserService) beanFactory.getBean("userService", "LCK", 23);
userService.queryUserInfo();
}

运行结果仍然准确,但是实际在实例化时,调用的是 clazz.getDeclaredConstructor().newInstance();
进行实例化
而不是 clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
。
因为在选择构造函数时,没有符合条件的构造函数,则默认传入的构造函数就是 null。
注入属性和依赖对象#
在学习这一节之前先解释两个概念:构造器注入、属性注入。
特性 | 构造器注入 (Constructor Injection) | 属性注入 (Setter/Field Injection) |
---|---|---|
注入时机 | Bean 实例化阶段(创建对象时完成) | Bean 实例化后阶段(对象已存在,后设置属性) |
依赖必要性 | 通常用于强制依赖(必须的依赖) | 通常用于可选依赖(非必须的依赖) |
不可变性 | 支持 final 字段(依赖不可变) |
字段通常非 final (依赖可变) |
循环依赖处理 | 无法直接解决(需配合其他机制) | 可以解决(通过三级缓存) |
代码健壮性 | 保证 Bean 初始化后完全可用,避免 NullPointer |
可能存在未初始化的依赖,需手动检查 |
Spring 官方推荐 | 优先推荐(尤其对于必需依赖) | 适用于可选依赖或遗留代码 |
测试友好性 | 易于通过构造函数传递 Mock 对象 | 需要额外的 Setter 方法或反射设置依赖 |
以下面的类来解释什么是强制依赖,什么是可选依赖。
对于下面的类来说,name 就是强制依赖,因为在创建 UserService 类时,必须向构造函数传入 name 的值才能创建。
而 age 就是可选依赖,因为 age 的值可以在创建 Bean 实例后,通过 setter 动态修改,不是必须传入的参数。
public class UserService {
private String name;
private Integer age;
// 强制依赖
public UserService(String name) {
this.name = name;
}
// 可选依赖
public void setAge(Integer age) {
this.age = age;
}
}
因此上一节的通过构造器实例化对象可以看作是构造器注入依赖,完成创建Bean实例。
为什么要有注入依赖对象#
如果A类依赖于B类的话,如下面所示,UserService 类中引用了 UserDao 对象,此时再想实例化 UserService 对象的话,就需要实例化 UserDao 对象,并将 UserDao 实例赋值给 UserService 的 userDao 属性:
public class UserService {
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;
}
}
如果没有用到注入属性和依赖对象的话,可以用下面的方法来实现对象引用的依赖:
@Test
public void test_Property() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
UserService userService = (UserService) beanFactory.getBean("userService");
// 实例化 UserDao 对象
UserDao userDao = (UserDao) beanFactory.getBean("userDao");
userService.setuId("10001");
// 引用类型赋值
userService.setUserDao(userDao);
userService.queryUserInfo();
}
在上面的代码中,没有将引用类型的赋值交给Bean来完成,而是自己手动创建Bean实例,然后手动赋值。
但是这种方法需要开发者自己手动管理所有的依赖关系。当项目规模扩大,Bean数量增多时,手动注入会变得非常繁琐,而且容易选漏某些依赖,导致运行时错误。此外,如果依赖关系发生变化,需要修改多处代码,违反了开闭原则。
代码结构#

- 本章节中需要新增加3个类,
BeanReference
(类引用)、PropertyValue
(属性值)、PropertyValues
(属性集合),分别用于类和其他类型属性填充操作。 - 另外改动的类主要是
AbstractAutowireCapableBeanFactory
,在 createBean 中补全属性填充部分。
BeanReference.class#
一个 Bean 可能依赖于其他的 Bean。BeanReference
类用于表示对另一个 Bean 的引用,它不会立即实例化所引用的 Bean,而是在合适的时机(通常是当前 Bean 实例化并需要注入依赖时)由 Spring 容器去查找和实例化被引用的 Bean。
BeanReference
类将 Bean 的定义和实例化过程解耦。在 Bean 定义阶段,只需要记录 Bean 之间的依赖关系,而不需要关心被引用 Bean 的具体实例化细节。Spring 容器在实例化 Bean 时,根据 BeanReference
去查找和创建相应的 Bean 实例,这样可以提高代码的灵活性和可维护性。
public class BeanReference {
private final String beanName;
public BeanReference(String beanName) {
this.beanName = beanName;
}
public String getBeanName() {
return beanName;
}
}
beanName 存储了被引用 Bean 的名称,Spring 容器可以根据这个名称来查找和获取对应的 Bean 实例。
AbstractAutowireCapableBeanFactory.class#
- 这个类主要包括三个方法:createBean、createBeanInstance、applyPropertyValues,这里我们主要关注 createBean 的方法中调用的 applyPropertyValues 方法。
- 在 applyPropertyValues 中,通过获取
beanDefinition.getPropertyValues()
循环进行属性填充操作,如果遇到的是 BeanReference,那么就需要递归获取 Bean 实例,调用 getBean 方法。 - 当把依赖的 Bean 对象创建完成后,会递归回现在属性填充中。BeanUtil.setFieldValue(bean, name, value) 是 hutool-all 工具类中的方法。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
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);
} 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();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}
/**
* Bean 属性填充
*/
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
try {
PropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
String name = propertyValue.getName();
Object value = propertyValue.getValue();
if (value instanceof BeanReference) {
// A 依赖 B,获取 B 的实例化
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
// 属性填充
BeanUtil.setFieldValue(bean, name, value);
}
} catch (Exception e) {
throw new BeansException("Error setting property values:" + beanName);
}
}
public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}
public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}
}
在 applyPropertyValues 方法中有一段代码如下,这段代码会先判断属性什么类型。这里体现出 BeanReference 的一个作用就是:
判断依赖的是基本类型还是对象类型
。如果是基本数据类型,就直接填充到属性中,如果是对象类型,则调用getBean获取依赖的对象,再填充属性。
if (value instanceof BeanReference) {
// A 依赖 B,获取 B 的实例化
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
PropertyValue.class#
这个类是存储类中单一属性的信息,包括属性名和属性值。
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;
}
}
PropertyValues.class#
但是考虑到一个类中会有多个属性,因此需要一个 PropertyValues 类来以列表的形式存储多个属性。
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;
}
}
BeanDefinition.class#
为了把属性一定交给 Bean 定义,所以这里填充了 PropertyValues 属性,同时把两个构造函数做了一些简单的优化,避免后面 applyPropertyValues 方法的 for 循环时还得判断属性填充是否为空。
public class BeanDefinition {
private Class beanClass;
private PropertyValues propertyValues;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
this.propertyValues = new PropertyValues();
}
public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
this.beanClass = beanClass;
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}
测试#
@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();
}
有人学到这里会有疑问:上面的测试代码也不简单啊!!!
自己手动注入依赖对象需要多次获取引用对象的 Bean,但是自动注入的话,也需要配置属性值,甚至还需要将这些属性包装成 PropertyValues,也不见得有多方便。
注意:目前完成的只是简化版的 spring 源码。实际上真正的 Spring 源码中,Spring 在背后处理了很多复杂情况,比如依赖解析、延迟加载、代理注入等,这些手动处理起来非常麻烦。
Spring配置方式的演进#
XML 配置(Spring 1.x)#
<bean id="userService" class="com.example.UserService">
<property name="uId" value="10001"/>
<property name="userDao" ref="userDao"/>
</bean>
- 优点:集中管理配置
- 痛点:配置冗长,与Java代码分离
注解驱动(Spring 2.5+)#
@Component
public class UserService {
@Value("10001")
private String uId;
@Autowired // 自动按类型注入
private UserDao userDao;
}
- 革命性改进:
- 配置与代码合一
- 依赖自动装配
- 无需手动操作
BeanDefinition
Java 配置(Spring 3.0+)#
@Configuration
public class AppConfig {
@Bean
public UserService userService(@Value("10001") String uId, UserDao userDao) {
UserService service = new UserService();
service.setUId(uId);
service.setUserDao(userDao);
return service;
}
}
- 平衡点:
- 保持配置的Java强类型特性
- 适合需要精细控制Bean创建的场景
上面提到的注解和Java配置都是高层抽象的结果。真正的底层实现还是BeanDefinition
、PropertyValues
等。
注解驱动最终会被转换为BeanDefinition
和PropertyValues
的底层操作。例如:
@Autowired
→ 生成AutowiredAnnotationBeanPostProcessor
@Value
→ 转换为PropertyValue
条目
资源加载器#
前面我们在实现注入属性和依赖对象时,提到需要手动包装类的属性和引用,过程过于繁琐,容易遗漏或出错。
下面我们尝试通过配置文件的方式简化创建过程。

- 如图中我们需要把步骤:2、3、4整合到Spring框架中,通过 Spring 配置文件的方式将 Bean 对象实例化。
- 接下来我们就需要在现有的 Spring 框架中,添加能解决 Spring 配置的读取、解析、注册Bean的操作。
首先我们需要实现一个资源加载器,能够读取 classPath、本地文件、云文件的内容。

本节我们定义了两个接口和三个类。
项目工程结构如下:

Resource.class#
其中 Resource 是资源的抽象和访问接口,简单写了三个实现类:ClassPathResource、UrlResource、FileSystemResource。
- FileSystemResource,文件系统资源的实现类
- ClassPathResource,classpath下资源的实现类
- UrlResource,对java.net.URL进行资源定位的实现类
public interface Resource {
InputStream getInputStream() throws IOException;
}
定义 Resource 接口,提供获取 InputStream 流的方法,接下来再分别实现三种不同的流文件操作:classPath、FileSystem、URL
public class ClassPathResource implements Resource {
private final String path;
public ClassPathResource(String path) {
this.path = path;
}
@Override
public InputStream getInputStream() throws IOException {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(this.path);
if (is == null) {
throw new FileNotFoundException(this.path + " cannot be opened because it does not exist");
}
return is;
}
}
这一部分的实现是用于通过 ClassLoader
读取ClassPath
下的文件信息,具体的读取过程主要是:this.getClass().getClassLoader().getResourceAsStream(this.path);
。
public class UrlResource implements Resource {
private final URL url;
public UrlResource(URL url) {
this.url = url;
}
@Override
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
try {
return con.getInputStream();
} catch (IOException ex) {
throw ex;
}
}
}
通过 HTTP 的方式读取云服务的文件。
public class FileSystemResource implements Resource {
private final String filePath;
public FileSystemResource(String filePath) {
this.filePath = filePath;
}
@Override
public InputStream getInputStream() throws IOException {
try {
Path path = new File(this.filePath).toPath();
return Files.newInputStream(path);
} catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
}
通过指定文件路径的方式读取文件信息,经常会读取一些txt、excel文件输出到控制台。
ResourceLoader.class#
ResourceLoader接口则是资源查找定位策略的抽象,DefaultResourceLoader是其默认实现类。
public interface ResourceLoader {
Resource getResource(String location);
}
public class DefaultResourceLoader implements ResourceLoader {
public static final String CLASSPATH_URL_PREFIX = "classpath:";
@Override
public Resource getResource(String location) {
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
//classpath下的资源
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
} else {
try {
//尝试当成url来处理
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException ex) {
//当成文件系统下的资源处理
return new FileSystemResource(location);
}
}
}
}
- 在获取资源的实现中,主要是把三种不同类型的资源处理方式进行了包装,分为:判断是否为ClassPath、URL以及文件。
- 虽然 DefaultResourceLoader 类实现的过程简单,但这也是设计模式约定的具体结果,像是这里不会让外部调用方法知道过多的细节,而是仅关心具体调用结果即可。
测试#
@Test
public void testResourceLoader() throws Exception {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
//加载classpath下的资源 在resources目录下放入hello.txt文件
Resource resource = resourceLoader.getResource("classpath:hello.txt");
InputStream inputStream = resource.getInputStream();
String content = IoUtil.readUtf8(inputStream);
System.out.println(content);
assertThat(content).isEqualTo("hello world");
//加载文件系统资源
resource = resourceLoader.getResource("src/test/resources/hello.txt");
assertThat(resource instanceof FileSystemResource).isTrue();
inputStream = resource.getInputStream();
content = IoUtil.readUtf8(inputStream);
System.out.println(content);
assertThat(content).isEqualTo("hello world");
//加载url资源
resource = resourceLoader.getResource("https://www.baidu.com");
assertThat(resource instanceof UrlResource).isTrue();
inputStream = resource.getInputStream();
content = IoUtil.readUtf8(inputStream);
System.out.println(content);
}

读取XML文件并注册BeanDefinition#
这一节相比于之前,增加了不少的接口的类。
项目结构:

按照功能模块,可以把新增的代码分为以下几个模块:
读取xml文件模块#

这个模块主要有三个文件:BeanDefinitionReader、AbstractBeanDefinitionReader、XmlBeanDefinitionReader
-
BeanDefinitionReader 是一个接口,它定义了读取
BeanDefinition
的基本规范和行为。getRegistry()
方法用于获取BeanDefinitionRegistry
,这个注册表用于管理和存储BeanDefinition
。getResourceLoader()
方法用于获取ResourceLoader
,它的作用是加载资源,例如配置文件等。loadBeanDefinitions
方法有三个重载形式,分别支持从Resource
对象、单个文件路径以及多个文件路径加载BeanDefinition
。
-
AbstractBeanDefinitionReader 是一个抽象类,它实现了
BeanDefinitionReader
接口。- 它包含了
BeanDefinitionRegistry
和ResourceLoader
两个成员变量,用于管理注册表和资源加载器。 - 提供了两个构造方法,一个构造方法接受
BeanDefinitionRegistry
和ResourceLoader
作为参数,另一个构造方法只接受BeanDefinitionRegistry
,并使用DefaultResourceLoader
作为默认的资源加载器。 - 实现了
getRegistry()
和getResourceLoader()
方法,用于返回相应的实例。 - 实现了
loadBeanDefinitions(String[] locations)
方法,该方法通过遍历locations
数组,调用loadBeanDefinitions(location)
方法来加载多个资源。
- 它包含了
-
XmlBeanDefinitionReader 是
AbstractBeanDefinitionReader
的具体实现类,专门用于读取 XML 格式的配置文件。- 定义了一些常量,如
BEAN_ELEMENT
、PROPERTY_ELEMENT
等,用于表示 XML 中的标签和属性。 - 提供了两个构造方法,调用父类的构造方法来初始化
BeanDefinitionRegistry
和ResourceLoader
。 - 实现了
loadBeanDefinitions(String location)
方法,该方法通过ResourceLoader
获取Resource
对象,然后调用loadBeanDefinitions(Resource resource)
方法。 - 实现了
loadBeanDefinitions(Resource resource)
方法,该方法从Resource
对象中获取输入流,调用doLoadBeanDefinitions(InputStream inputStream)
方法来解析 XML 文件。 doLoadBeanDefinitions(InputStream inputStream)
方法负责具体的 XML 文件解析工作,创建BeanDefinition
并将其注册到BeanDefinitionRegistry
中。
- 定义了一些常量,如
这三个接口和类用到了设计模式中的模板模式,AbstractBeanDefinitionReader
作为抽象类,为具体的 BeanDefinitionReader
实现类提供了一个通用的框架。它实现了一些通用的方法,如 loadBeanDefinitions(String[] locations)
,而将一些具体的实现留给子类,例如 loadBeanDefinitions(String location)
和 loadBeanDefinitions(Resource resource)
方法。这样可以避免子类重复实现这些通用的逻辑,提高代码的复用性。
XmlBeanDefinitionReader.class#
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public static final String BEAN_ELEMENT = "bean";
public static final String PROPERTY_ELEMENT = "property";
public static final String ID_ATTRIBUTE = "id";
public static final String NAME_ATTRIBUTE = "name";
public static final String CLASS_ATTRIBUTE = "class";
public static final String VALUE_ATTRIBUTE = "value";
public static final String REF_ATTRIBUTE = "ref";
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws BeansException {
ResourceLoader resourceLoader = getResourceLoader();
Resource resource = resourceLoader.getResource(location);
loadBeanDefinitions(resource);
}
@Override
public void loadBeanDefinitions(Resource resource) throws BeansException {
try {
InputStream inputStream = resource.getInputStream();
try {
doLoadBeanDefinitions(inputStream);
} finally {
inputStream.close();
}
} catch (IOException ex) {
throw new BeansException("IOException parsing XML document from " + resource, ex);
}
}
protected void doLoadBeanDefinitions(InputStream inputStream) {
Document document = XmlUtil.readXML(inputStream);
Element root = document.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i) instanceof Element) {
if (BEAN_ELEMENT.equals(((Element) childNodes.item(i)).getNodeName())) {
//解析bean标签
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute(ID_ATTRIBUTE);
String name = bean.getAttribute(NAME_ATTRIBUTE);
String className = bean.getAttribute(CLASS_ATTRIBUTE);
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("Cannot find class [" + className + "]");
}
//id优先于name
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
//如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition(clazz);
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (bean.getChildNodes().item(j) instanceof Element) {
if (PROPERTY_ELEMENT.equals(((Element) bean.getChildNodes().item(j)).getNodeName())) {
//解析property标签
Element property = (Element) bean.getChildNodes().item(j);
String nameAttribute = property.getAttribute(NAME_ATTRIBUTE);
String valueAttribute = property.getAttribute(VALUE_ATTRIBUTE);
String refAttribute = property.getAttribute(REF_ATTRIBUTE);
if (StrUtil.isEmpty(nameAttribute)) {
throw new BeansException("The name attribute cannot be null or empty");
}
Object value = valueAttribute;
if (StrUtil.isNotEmpty(refAttribute)) {
value = new BeanReference(refAttribute);
}
PropertyValue propertyValue = new PropertyValue(nameAttribute, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
if (getRegistry().containsBeanDefinition(beanName)) {
//beanName不能重名
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
//注册BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
在 XmlBeanDefinitionReader 中,最主要的实现逻辑就是 doLoadBeanDefinitions
方法中。
以下面的xml文件内容为例,来解释 doLoadBeanDefinitions 方法:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="person" class="org.springframework.test.ioc.bean.Person">
<property name="name" value="derek"/>
<property name="car" ref="car"/>
</bean>
<bean id="car" class="org.springframework.test.ioc.bean.Car">
<property name="brand" value="porsche"/>
</bean>
</beans>
1. 读取 XML 文件并获取根元素#
Document document = XmlUtil.readXML(inputStream);
Element root = document.getDocumentElement();
XmlUtil.readXML(inputStream)
:调用XmlUtil
工具类的readXML
方法,将输入流inputStream
中的 XML 数据解析为一个Document
对象,该对象代表整个 XML 文档。document.getDocumentElement()
:从Document
对象中获取 XML 文档的根元素,后续的解析操作将从根元素开始。
2. 遍历根元素的子节点#
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i) instanceof Element) {
if (BEAN_ELEMENT.equals(((Element) childNodes.item(i)).getNodeName())) {
// 解析bean标签
...
}
}
}
root.getChildNodes()
:获取根元素的所有子节点,存储在NodeList
中。- 通过
for
循环遍历NodeList
,使用instanceof
判断当前节点是否为Element
类型(即 XML 元素)。 - 若当前节点是
Element
类型,且节点名称为BEAN_ELEMENT
(这里为<bean>
),则进入<bean>
标签的解析逻辑。
3. 解析<bean>
标签#
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute(ID_ATTRIBUTE);
String name = bean.getAttribute(NAME_ATTRIBUTE);
String className = bean.getAttribute(CLASS_ATTRIBUTE);
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("Cannot find class [" + className + "]");
}
bean.getAttribute(ID_ATTRIBUTE)
、bean.getAttribute(NAME_ATTRIBUTE)
、bean.getAttribute(CLASS_ATTRIBUTE)
:分别获取<bean>
标签的id
、name
和class
属性值。Class.forName(className)
:通过反射机制,根据类名获取对应的Class
对象。若类名不存在,则抛出ClassNotFoundException
异常,并将其包装成BeansException
异常抛出。
4. 确定 Bean 的名称#
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
// 如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
- 优先使用
id
属性值作为 Bean 的名称,若id
为空,则使用name
属性值。 - 若
id
和name
都为空,则将类名的第一个字母转为小写后作为 Bean 的名称。
5. 创建BeanDefinition
对象#
BeanDefinition beanDefinition = new BeanDefinition(clazz);
- 创建一个
BeanDefinition
对象,将之前获取的Class
对象传入,用于存储 Bean 的定义信息。
6. 解析<property>
标签#
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (bean.getChildNodes().item(j) instanceof Element) {
if (PROPERTY_ELEMENT.equals(((Element) bean.getChildNodes().item(j)).getNodeName())) {
// 解析property标签
Element property = (Element) bean.getChildNodes().item(j);
String nameAttribute = property.getAttribute(NAME_ATTRIBUTE);
String valueAttribute = property.getAttribute(VALUE_ATTRIBUTE);
String refAttribute = property.getAttribute(REF_ATTRIBUTE);
if (StrUtil.isEmpty(nameAttribute)) {
throw new BeansException("The name attribute cannot be null or empty");
}
Object value = valueAttribute;
if (StrUtil.isNotEmpty(refAttribute)) {
value = new BeanReference(refAttribute);
}
PropertyValue propertyValue = new PropertyValue(nameAttribute, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
- 遍历
<bean>
标签的子节点,找到所有的<property>
标签。 - 获取
<property>
标签的name
、value
和ref
属性值。 - 若
name
属性为空,则抛出BeansException
异常。 - 若
ref
属性不为空,则创建一个BeanReference
对象,代表对另一个 Bean 的引用;否则,直接使用value
属性值。 - 创建一个
PropertyValue
对象,将属性名和属性值封装起来,并添加到BeanDefinition
的PropertyValues
中。
7. 检查 Bean 名称是否重复并注册BeanDefinition
#
if (getRegistry().containsBeanDefinition(beanName)) {
// beanName不能重名
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
// 注册BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
getRegistry().containsBeanDefinition(beanName)
:检查BeanDefinitionRegistry
中是否已经存在相同名称的BeanDefinition
,若存在则抛出BeansException
异常。getRegistry().registerBeanDefinition(beanName, beanDefinition)
:将解析得到的BeanDefinition
对象注册到BeanDefinitionRegistry
中,以便后续 Spring 容器使用。
测试#
先定义xml文件,定义类的属性和类之间的依赖关系:
<?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="01"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
定义UserDao类,在该类中定义了键值之间的对应关系:
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
static {
hashMap.put("01", "LCK_TEST01");
hashMap.put("02", "LCK_TEST02");
hashMap.put("03", "LCK_TEST03");
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
定义UserService类,表面UserService类的属性以及与UserDao类之间的引用关系:
public class UserService {
private String uId;
private UserDao userDao;
public String queryUserInfo() {
return 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;
}
}
测试用例:
private DefaultResourceLoader resourceLoader;
@Before
public void init() {
resourceLoader = new DefaultResourceLoader();
}
@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);
}
测试结果:

作者:maoxianjia
出处:https://www.cnblogs.com/mxj-lck/p/18729621
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
转载请注明原处
本文来自博客园,作者:maoxianjia,转载请注明原文链接:https://www.cnblogs.com/mxj-lck/p/18729621
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律