【simple-spring】Bean 容器实现

61 阅读48分钟

前言

实践出真知 -- 如果真想从 spring 中学到东西,你一定要动手写一个自己的 spring。

大家好,我是 mengzhishang,上面是我写完这两篇文章后的感悟,像很多人一样,我也断断续续看过一些 spring 的原理,大多是 Bean 的生命周期介绍,三层缓存解决循环依赖这些,坦白的说很难去记忆和理解。因为 spring 的设计具有极强的广度(领域划分)和深度(设计模式),想从“管”中窥得 “spring” 这个豹真的很难

下定决心手撸这个 simple-spring 真的要感谢小傅哥,他的系列文章让一切变得简单和可能。如果只是对着 spring-framework 这个代码库去手写一个 spring 是极难的,因为你不知道要在哪里做非核心功能的取舍,也比较难去领悟 spring 颇多设计的巧妙。然而结合文档再去看源码,手写一个只有核心功能的版本,对大多数人来说是可接受的。

取自开源,回报以开源。整个 simple-spring 是在小傅哥的【spring 手撸专栏】基础上重新实现,框架设计几乎没有变化,和小傅哥原作的主要差异点:

  • 对本人在学习和实践该专栏的过程中一些不解和设计上觉得有些跳跃的地方进行了个人见解的补充和层次设计;
  • 每个大模块都进行了原创的图形化解释,帮助自己和读者更直观地理解;
  • 代码对接口和方法提供了简化版的全英文注释,可以更好体会 spring-framework 设计者的最初想法;
  • AOP 模块和小傅哥的原文内容有较大差别;文章从 AOP Alliance 接口框架开始讲解,模块实现更接近 Spring;
  • 代码仓库每个分支对应一个文章章节,更方便 spring 学习者们参考对应分支学习和实践;

建议:学习过程中理解某个类或者方法的功能时,最好根据名称去 spring-framework 中查看一遍注释和源码,而不是只看文中简单版的实现;

仓库地址simple-spring

整体结构:本系列文章我们将分四个部分介绍,分别是两部分 spring 最核心的基础能力:Bean 容器AOP,以及 spring 在面向用户友好的演进中体感最明显的注解化能力,最后一部分我将介绍自己在公司做启动耗时优化专项时对 spring 做的Bean 加载优化工作。 spring.png

开始前我们先通过 IoC 引入 Bean 容器。IoC(Inversion of Control)是控制反转。控制反转是种设计思想,在学习设计模式时我们知道:控制反转指的是框架代替用户控制程序,主要是利用了抽象的思想,系统层次之间不依赖具体而是依赖抽象。在使用时通过多态实现框架的灵活性;

Spring 中处处可见 IoC 思想(这也造就了其框架的定位),本节我们将从 spring 中对象创建的控制反转学习起。相比于程序通过new来主动创建对象,Spring将对象创建和管理的权利交给了容器,容器在程序运行时动态的创建对象和维护对象之间的关系。在实现时,这种和用户硬编码的解耦可以通过配置或者注解来实现。

01 创建一个Bean容器

目标

Spring Bean

Bean是指一个由Spring容器管理的对象,该对象的创建、销毁等生命周期完全交给容器管理,优势是将对象的创建和管理和业务逻辑分离。

Spring Bean容器

spring-01.png

Bean 容器的基本功能就是提供获取 Bean 的能力,这一章主要就 Spring 如何完整的获取一个 Bean 做学习和实践,主要包括了 Bean 的注册、创建(实例化、属性注入)和获取。其中,框架在控制反转的思想基础上,利用了大量设计模式去铺垫框架的可扩展能力,包括模版方法模式、策略模式;也按照功能职责做了细致的模块和层次划分,个人认为是高内聚低耦合的思想体现。

这一 part 的具体实现基本和小傅哥原文一致,因此介绍的比较简单直接。

1 实现一个最简单的Bean容器

分支:github.com/shengdoupi/…

由Bean的定义可知,Bean 容器是用来管理 Bean 对象的,容器需要通过一些元数据来知悉如何管理这个 Bean ,比如如何被实例化等,我们把这些元数据看作是 Bean 的定义。当一个 Bean 被定义好后,交给Spring Bean 容器统一创建、使用和销毁。

因此,一个 Bean 容器需要具有定义 Bean、依据定义注册 Bean 和获取 Bean 的能力。我们按照下述方法实现一个简单的 Bean 容器:

package io.github.shengdoupi.ioc;

/**
 * @author zhoukehh
 * @date 2024/3/21
 * @description Bean definition.
 */
public class BeanDefinition {
    private Class beanClass;
    
    public BeanDefinition(Class beanClass) {
        this.beanClass = beanClass;
    }
    
    public Class getBeanClass() {
        return this.beanClass;
    }
    
    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }
}
  • 定义:令 BeanDefinition 作为 Bean 的元数据文件;其中包含 Bean 对应的类 Class 文件,用于创建实例;默认约束 Bean 为单例,即容器中只能有一个实例。
package io.github.shengdoupi.ioc;

import ...

/**
 * @author zhoukehh
 * @date 2024/3/21
 * @description
 */
public class BeanFactory {
    
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    
    private Map<String, Object> singletonObjects = new HashMap<>();
    
    /**
     * Register bean definition.
     */
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }
    
    /**
     * Get singleton bean.
     */
    public Object getBean(String beanName) {
        Object singletonObject = singletonObjects.get(beanName);
        if (singletonObject != null) {
            return singletonObject;
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        singletonObject = createBean(beanDefinition);
        singletonObjects.put(beanName, singletonObject);
        return singletonObject;
    }
    
    /**
     * Create singleton bean.
     */
    private Object createBean(BeanDefinition beanDefinition) {
        Object beanObject = null;
        try {
            beanObject = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            return beanObject;
        }
        return beanObject;
    }
}
  • 注册:将 BeanDefinition 注册到容器中;
  • 获取:从容器中依据类名获取 Bean 实例,如果没有则先创建单例实例,如果有则直接返回单例实例;我们可以使用 HashMap 来存储单例。

2 实现模块划分更清晰的Bean容器

分支:github.com/shengdoupi/…

相比于我们上述的设计,Spring 设计了一套通过继承、封装做了更清晰的模块划分的容器架构,包括:

  1. 引入单例对象注册表,定义单例对象的注册、获取方法;
  2. 引入 BeanDefinition 注册表,定义 BeanDefinition 的注册方法。
  3. 使用模版方法模式,定义 AbstractBeanFactory 抽象工厂类,规范了获取 Bean 的模版流程,但对涉及的具体方法不做实现,留给子类实现;
  4. 在子类 AbstractAutowireCapableBeanFactory 中实现 createBean 方法;
  5. 在 DefaultListableBeanFactory 类中,新增 BeanDefinition 注册、获取方法的实现,是一个基本成熟的 Bean 工厂;

具体的模块如下:

package io.github.shengdoupi.springframework.beans.factory;

import io.github.shengdoupi.springframework.beans.BeansException;

/**
 * @author zhoukehh
 * @date 2024/3/26
 * @description Bean factory.
 */
public interface BeanFactory {
    
    /**
     * Get bean.
     */
    Object getBean(String beanName) throws BeansException;
}
  • BeanFactory: 接口, 规范获取 Bean 的方法 getBean();
package io.github.shengdoupi.springframework.beans.factory.config;

/**
 * @author zhoukehh
 * @date 2024/3/26
 * @description
 */
public interface SingletonBeanRegistry {
    /**
     * Get singleton.
     */
    Object getSingleton(String beanName);
}
  • SingletonBeanRegistry: 接口,规范管理单例 Bean 对象的方法,定义 getSingleton() ;
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @author zhoukehh
 * @date 2024/3/26
 * @description
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    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);
    }
}
  • DefaultSingletonBeanRegistry: 通用管理单例 bean 实例的类,实现 SingletonBeanRegistry 接口,实现了 getSingleton() 和 addSingleton() 方法;
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @author zhoukehh
 * @date 2024/3/26
 * @description
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
    @Override
    public Object getBean(String beanName) throws BeansException{
        Object bean = getSingleton(beanName);
        if (bean != null) {
            return bean;
        }
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        return creatBean(beanName, beanDefinition);
    }
    
    /**
     * Get bean definition.
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
    
    /**
     * Create bean.
     */
    protected abstract Object creatBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
    
}
  • AbstractBeanFactory: 实现 BeanFactory 的抽象类,使用模版方法模式,通过给定的 Bean 定义获取 Bean 实例的通用模版方法 getBean();定义了通过 Bean 名称获取 Bean 定义的抽象方法 getBeanDefinition(), 以及通过继承 DefaultSingletonBeanRegistry,提供了单例 Bean 实例的管理机制;
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @author zhoukehh
 * @date 2024/3/26
 * @description Abstract autowire capable bean factory.
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
    
    @Override
    public Object creatBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        Object bean = null;
        try {
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException exception) {
            throw new BeansException("Create bean from bean definition exception.", exception);
        }
        addSingleton(beanName, bean);
        return bean;
    }
}
  • AbstractAutowireCapableBeanFactory:抽象类继承自 AbstractBeanFactory,主要实现了getBean() 内创建 Bean 实例的方法 createBean();
package io.github.shengdoupi.springframework.beans.factory.support;

import io.github.shengdoupi.springframework.beans.factory.config.BeanDefinition;

/**
 * @author zhoukehh
 * @date 2024/3/27
 * @description
 */
public interface BeanDefinitionRegistry {
    
    /**
     * Register bean definition.
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}
  • BeanDefinitionRegistry: BeanDefinition 的注册表接口,主要定义了 BeanDefinition 的注册方法registerBeanDefinition();
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @author zhoukehh
 * @date 2024/3/26
 * @description Default listable bean factory.
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {
    
    Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    
    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new BeansException("Bean definition null exception.");
        }
        return beanDefinitionMap.get(beanName);
    }
    
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }
}
  • DefaultListableBeanFactory: 继承了AbstractAutowireCapableBeanFactory, 同时实现BeanDefinitionRegistry接口,实现了registerBeanDefinition(),是一个成熟的基于BeanDefinition元数据的Bean管理容器;

3 引入实例化策略

分支:github.com/shengdoupi/…

上述DefaultListableBeanFactory虽然已经是一个可以注册、创建和获取的Bean工厂,但一个很大的局限是可以管理的Bean只能是无参构造函数实例化得到的。如果Bean对应的类包含有参构造函数,Bean工厂要如何在创建Bean时通过有参构造函数进行实例化呢?

  • Java反射生成

Java反射可以通过getDeclaredConstructor()或getConstructor()方法,依据入参类型获取对应的构造器,进一步使用构造器的newInstance()方法创建对象;

Spring基于上述方案提供了两个实例化策略:

package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @description Simple instantiation strategy.
 */
public class SimpleInstantiationStrategy implements InstantiationStrategy {
    
    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor<?> ctor, Object... args) {
        return instantiateClass(ctor, args);
    }
    
    protected Object instantiateClass(Constructor<?> ctor, Object... args) {
        try {
            if (null == ctor) {
                throw new BeansException("Constructor must not be null");
            }
            ctor.setAccessible(true);
            int parameterCount = ctor.getParameterCount();
            if (parameterCount == 0) {
                return ctor.newInstance();
            }
            return ctor.newInstance(args);
        } catch (InvocationTargetException | InstantiationException | IllegalAccessException e){
            throw new BeansException("Instantiate exception.", e);
        }
    }
}
  • SimpleInstantiationStrategy,利用反射实现通过有参构造函数的实例化;
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @description Cglib subclassing instantiation strategy.
 */
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy{
    
    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor<?> ctor, Object... args) {
        if (null == ctor) {
            throw new BeansException("Constructor must not be null");
        }
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (0 == ctor.getParameterCount()) {
            return enhancer.create();
        }
        return enhancer.create(ctor.getParameterTypes(), args);
    }
}
  • CglibSubclassingInstantiationStrategy,利用Cglib框架动态创建对象,Cglib是一个封装了ASM可以操纵字节码的框架。在Spring的设计考虑中,主要是利用Cglib动态生成子类的能力,实现实例化时支持方法重写,从命名中也能发现端倪。CglibSubclassingInstantiationStrategy继承自SimpleInstantiationStrategy,只是实现了定义在SimpleInstantiationStrategy中的支持方法重写的实例化抽象方法。

在simple-spring中,不提供支持方法重写的实例化功能。CglibSubclassingInstantiationStrategy作为一个和SimpleInstantiationStrategy平行的类,两者同样实现自InstantiationStrategy接口。

package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @description Abstract autowire capable bean factory.
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
    
    @Override
    public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
        Object bean = null;
        ...
        bean = createBeanInstance(beanName, beanDefinition, args);
        ...
    }
    
    /**
     * Instantialize bean.
     */
    protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
        // Get constructor.
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] ctors = beanClass.getDeclaredConstructors();
        Object[] argsToUse = null;
        Constructor ctorToUse = null;
        if (null != args) {
            argsToUse = args;
        } else {
            argsToUse = new Object[0];
        }
        for (Constructor<?> ctor : ctors) {
            if (ctor.getParameterCount() == argsToUse.length) {
                ctorToUse = ctor;
                break;
            }
        }
        // Instantiation.
        return instantiationStrategy.instantiate(beanDefinition, beanName, ctorToUse, argsToUse);
    }
}

  • 在 Bean 工厂中引入实例化策略;

实现过程中发现cglib不支持JDK17, 可以通过将编译工具改为JDK1.8

4 属性注入

分支:github.com/shengdoupi/…

Bean内部包含属性时,如果该属性本身也是Bean容器托管的Bean,如何将该属性注入到Bean对象中呢?

首先,容器需要知道一个Bean包含哪些属性,这个可以通过BeanDefinition来声明;

其次,在Bean实例化后,我们依据BeanDefinition中的声明依次将依赖的属性从Bean容器中取出,注入到Bean属性中;那么Spring如何实现?

package io.github.shengdoupi.springframework.beans;

import com.sun.istack.internal.NotNull;

/**
 * @description Property value.
 */
public class PropertyValue {
    private String name;
    
    @NotNull
    private Object value;
    
    public PropertyValue(String name, Object value) {
        this.name = name;
        this.value = value;
    }
    ...
}
  • 定义PropertyValue类,本质是一个k-v,用来在定义BeanDefinition时,存储属性的名称和Bean引用;
package io.github.shengdoupi.springframework.beans;

import ...

/**
 * @description Property value.
 */
public class PropertyValues {
    private List<PropertyValue> propertyValues = new ArrayList<>();
    
    public void addPropertyValue(PropertyValue propertyValue) {
        this.propertyValues.add(propertyValue);
    }
    
    public PropertyValue[] getPropertyValues() {
        return this.propertyValues.toArray(new PropertyValue[0]);
    }
}
  • 定义PropertyValues类,存储PropertyValue列表,至于为什么不直接用一个Map类型来存储,Spring中给的解释是List可以维护顺序,便于按照顺序执行属性注入。
package io.github.shengdoupi.springframework.beans.factory.config;

/**
 * @description
 */
public interface BeanReference {
    /**
     * Return the target bean name this reference points to.
     */
    String getBeanName();
}
  • 定义BeanReference接口,表示对Bean名称的引用,作为PropertyValue里的v,可以用于表示该属性对应的Bean对象名称。
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @description Abstract autowire capable bean factory.
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
    
    @Override
    public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanName, beanDefinition, args);
            applyPropertyValues(beanDefinition, beanName, bean);
        } catch (Exception e) {
            throw new BeansException("Instantiate bean failed.", e);
        }
        addSingleton(beanName, bean);
        return bean;
    }
    
    ...
    
    protected void applyPropertyValues(BeanDefinition beanDefinition, String beanName, Object bean) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            if (null == propertyValues) {
                return;
            }
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                if (value instanceof BeanReference) {
                    BeanReference beanReference = (BeanReference) propertyValue.getValue();
                    value = getBean(beanReference.getBeanName());
                }
                setFieldValue(beanName, bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("Apply property values error", e);
        }
        
    }
    
    protected void setFieldValue(String beanName, Object bean, String propertyName, Object propertyValue) {
        try {
            Class clazz = getBeanDefinition(beanName).getBeanClass();
            Field field = clazz.getDeclaredField(propertyName);
            field.setAccessible(true);
            field.set(bean, propertyValue);
        } catch ( NoSuchFieldException | SecurityException e) {
            throw new BeansException("Reflection get field error " + beanName, e);
        } catch ( IllegalArgumentException | IllegalAccessException e) {
            throw new BeansException("Reflection set field value error", e);
        }
    }
}
  • 实例化时,如果一个对象有Bean对象作为的属性,那么从Bean工厂获取该Bean对象属性,通过反射进行填充;获取Bean对象属性时,通过递归的方式由下向上实例化Bean。
  • 我们暂时不考虑循环依赖的问题。

总结

目前为止,我们已经拥有了一个功能完备的 Bean 工厂,包括 BeanDefinition 的注册、Bean 的实例化、Bean 的属性注入。但现在的能力范围让我们在使用时有很多局限性,比如:

  1. 要想注册一个 Bean,首先需要通过代码来创建一个 BeanDefinition 对象,使用者要写大量 new 的代码,注册一个 Bean 较为繁琐,要想注册多个 Bean 那就更难为使用者了;
  2. 作为一个框架,扩展点是很需要的,使用者可能需要在一个定义好的 Bean 的获取流程中,做一些修改操作;
  3. 我们发现使用者对 Bean 工厂的使用流程是非常一致的:注册 BeanDefinition、创建 Bean 、一些扩展能力,当然还有获取 Bean,除了获取 Bean 外,其他的流程可以抽象出一个模版出来,每次有变更时(比如BeanDefinition修改、扩展能力新增等),自动走一遍这个模版,然后使用者只需不断调用 getBean("xx") 即可;

02 配置化、自动化和扩展能力

第一章我们实现了 Bean 工厂最基本的功能:管理 Bean。但在单元测试时我们发现使用起来和真正的 Spring 体验差距有点太大:

  • 注册一个 Bean 需要手写好多代码,都比我每次 new 对象都复杂,能否通过配置化实现呢?
  • 从 Bean 的注册到获取使用者要多次和 BeanFactory 进行交互,需要使用者理解 Spring 的内部逻辑,能否通过自动化流程使得对使用者更黑盒一些?
  • 正如开头讲的一样,在实现的过程中可以发现 Spring 的设计处处为扩展能力做了铺垫,包括多种设计模式的使用、模块的层次划分等;那么在 Spring 的设计目标中,究竟为 Bean 工厂做了哪些能力的扩展呢?

这一章我们就 Spring 中这三个问题的答案进行学习和实践。

spring-02 (1).png

1 资源加载器解析文件注册对象

分支:github.com/shengdoupi/…

目标

考虑一个问题:如何将我们需要管理的Bean以配置的形式,交付给BeanFactory管理?而不是显式的在代码里去注册BeanDefinitoin.

Spring中提供了XML文件的形式对Bean进行配置,我们需要一个对Bean配置的读取、解析和使用的模块。

设计

package io.github.shengdoupi.springframework.core.io;

import ...

/**
 * @description Resource
 */
public interface Resource {
    
    /**
     * Get resource input stream.
     */
    InputStream getInputStream() throws IOException;
}
package io.github.shengdoupi.springframework.core.io;

/**
 * @description Resource loader.
 */
public interface ResourceLoader {
    String CLASSPATH_URL_PREFIX = "classpath:";
    
    /**
     * Get resource.
     * @param location
     * @return Resource.
     */
    Resource getResource(String location);
}

  • 为了实现资源加载器对XML文件进行读取和加载,Spring中定义了接口Resource和ResourceLoader对资源和资源加载进行规范。
package io.github.shengdoupi.springframework.core.io;

import ...

/**
 * @description Class path resource.
 */
public class ClassPathResource implements Resource {
    String path;
    
    ClassLoader classLoader;
    
    public ClassPathResource(String path) {
        this(path, null);
    }
    
    public ClassPathResource(String path, ClassLoader classLoader) {
        this.path = path;
        this.classLoader = classLoader;
    }
    
    
    @Override
    public InputStream getInputStream() throws IOException {
        InputStream is = classLoader.getResourceAsStream(path);
        return is;
    }
}

package io.github.shengdoupi.springframework.core.io;

import ...

/**
 * @description Default resource loader.
 */
public class DefaultResourceLoader implements ResourceLoader{
    
    @Override
    public Resource getResource(String location) {
        if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
        }
        ...
    }
}
  • 具体对资源加载器的实现,Spring中提供了 ClassPath、系统文件和URL文件类型资源的具体实现,并在DefaultResourceLoader中做具体的调用。
package io.github.shengdoupi.springframework.beans.factory.support;

import ...

/**
 * @description Xml bean definition reader.
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            doLoadBeanDefinitions(resource.getInputStream(), resource);
        } catch (IOException e) {
            throw new BeansException("Get input stream error", e);
        }
    }
   
    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        Resource resource = getResourceLoader().getResource(location);
        loadBeanDefinitions(resource);
    }
    
    private void doLoadBeanDefinitions(InputStream inputStream, Resource resource) {
        try {
            // parse doc
            Document doc = XMLUtils.read(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;
                }
                // parse element
                Element bean = (Element) childNodes.item(i);
                String id = bean.getAttribute("id");
                String name = bean.getAttribute("name");
                String className = bean.getAttribute("class");
                // get clazz
                Class<?> clazz = Class.forName(className);
                // beanName
                String beanName = StringUtils.isNotBlank(id) ? id : name;
                // BeanDefinition
                BeanDefinition beanDefinition = new BeanDefinition(clazz);
                NodeList beanChildNodes = bean.getChildNodes();
                PropertyValues propertyValues = new PropertyValues();
                beanDefinition.setPropertyValues(propertyValues);
                for (int j = 0; j < beanChildNodes.getLength(); ++j) {
                    if (!(beanChildNodes.item(j) instanceof Element)) {
                        continue;
                    }
                    if (!"property".equals(beanChildNodes.item(j).getNodeName())) {
                        continue;
                    }
                    // parse property
                    Element property = (Element) beanChildNodes.item(j);
                    String attrName = property.getAttribute("name");
                    String attrValue = property.getAttribute("value");
                    String attrRef = property.getAttribute("ref");
                    Object value = StringUtils.isNotBlank(attrRef) ? (BeanReference) () -> attrRef : attrValue;
                    propertyValues.addPropertyValue(new PropertyValue(attrName, value));
                }
                if (getRegistry().containsBeanDefinition(beanName)) {
                    throw new BeansException("Duplicate bean name: " + beanName);
                }
                // Register bean definition.
                getRegistry().registerBeanDefinition(beanName, beanDefinition);
            }
        } catch (Exception e) {
            throw new BeansException("Load bean definition error", e);
        }
    }
}
  • 加载资源后,对资源的使用主要包括:解析获得BeanDefinition,并注册到容器的注册表中。Spring定义了BeanDefinitionReader接口、抽象类AbstractBeanDefinitionReader、实现类XmlBeanDefinitionReader,分别定义了接口功能、非接口功能外的注册Bean组件填充,以及具体的业务实现。

2 应用上下文实现

分支:github.com/shengdoupi/…

目标

目前为止我们在使用simple-spring时,注册和获得一个Bean需要和simple-spring做两次交互,即XmlBeanDefinitionReader读取配置和注册Bean,以及DefaultListableBeanFactory获取Bean;

我们知道,Spring提供了一些可扩展能力,比如BeanFactoryPostProcessor和BeanPostProcessor接口,用户实现这两个接口的逻辑,Spring即可在注册、实例化Bean时执行用户扩展的逻辑。

那当我们使用了扩展接口时,在一次注册和获取Bean的过程中和Spring的交互变为了四次;显然,频繁的和框架交互是非用户友好的,用户需要感知Spring太多的内部逻辑。因此,我们需要一个入口,从入口处触发 Spring 扫描、解析、注册和实例化,以及执行扩展能力,屏蔽掉用户对 Spring 内部逻辑的感知。在 Spring 中,这个入口即是应用上下文。

设计

  • BeanFactoryPostProcessor:扩展接口,在Bean注册到BeanFactory后修改Bean的定义;
  • BeanPostProcessor:扩展接口,在Bean实例化后修改Bean实例,甚至可以替换Bean对象;
  • ApplicationContext:Spring IoC 的上下文操作类,将Bean定义的加载、解析、注册和Bean的实例化,以及过程中的扩展能力执行都融到内部,让用户只需和它交互而不用感知 IoC 的内部逻辑。

在实现上述功能前,我们首先按照spring的结构新增一些配置类,主要包括:

  • 给BeanFactory新增依据名称和指定类型获取Bean的方法:   T getBean(String beanName, Class requiredType), 并在AbstractBeanFactory中做相应模版实现;
  • HierarchicalBeanFactory: 在spring中被作为层次Bean工厂,提供了获取parent工厂的方法,以及判断本地工厂是否包含某个bean的方法,simple-spring不涉及层次Bean工厂,因此这里不做定义;
  • ListableBeanFactory:可列举全部bean实例的BeanFactory,而不是只能通过beanName去获取指定bean实例。提供了包括获取工厂内全部已注册Bean名称的方法,以及指定bean类型下全部bean实例的功能;
  • ConfigurableBeanFactory:配置接口,simple-spring在该接口定义addBeanPostProcessor方法;
  • AutowireCapableBeanFactory: 配置接口,为实现了该接口的BeanFactory提供了自动注入的能力;提供了包括指定类型创建新Bean,对已实例化的bean自动注入Bean属性等方法;
  • ConfigurableListableBeanFactory:配置接口,大部分可列举bean工厂需要实现的接口。

实现

  1. 创建 BeanFactoryPostProcessor 接口,定义 postProcessBeanFactory 方法;
/**
 * @author zhoukehh
 * @date 2024/4/27
 * @description Factory hook that allows for custom modification of an application context's bean definitions.
 *
 */
public interface BeanFactoryPostProcessor {
    
    /**
     * Modify the application context's bean factory after its standard initialization.
     * All bean definitions has been loaded, but no bean has been instantiated yet.
     * This allows overriding or adding properties for beans' definition.
     * @param beanFactory
     * @throws BeansException
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

这个方法在 ApplicationContext 中被触发,在BeanDefinition加载完成后、Bean实例化之前,修改或重写BeanDefinition属性;

  1. 定义BeanPostProcessor接口;

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description Factory hook that allows for custom modification of new bean instances.
     */
    public interface BeanPostProcessor {
        
        /**
         * Modify a bean before its initialization.
         * @param bean
         * @param beanName
         * @return
         * @throws BeansException
         */
        @Nullable
        default Object postProcessBeanBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
        
        /**
         * Modigy a bean after its initialization.
         * @param bean
         * @param beanName
         * @return
         * @throws BeansException
         */
        @Nullable
        default Object postProcessBeanAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }

这个接口提供了修改实例化Bean对象的能力,分别定义了在Bean初始化方法执行之前和之后修改Bean的方法。在 ApplicationContext 中,当 Bean 实例化之后,初始化之前被执行;

  1. 定义上下文接口;

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description Central interface to provide configuration for an application.
     * An application context provides:
     * 1. Bean factory methods for accessing application components.
     * 2. The ability to load file resources.
     */
    public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader {
    }

上下文接口是对一个程序提供Bean配置的中心接口,它继承了BeanFactory和ResourceLoader,从而可以提供获取应用内 component(即bean)的方法,以及资源加载的能力。

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     * Provides facilities to configure an application context in addition to the application context client methods
     * in ApplicationContext interface.
     * Configuration  and lifecycle methods are encapsulated here.
     */
    public interface ConfigurableApplicationContext extends ApplicationContext{
        
        /**
         * Load or refresh the persistent representation of the configuration.
         * @throws BeansException
         */
        void refresh() throws BeansException;
    }

ApplicationContext负责提供和用户交互的方法, ConfigurableApplicationContext 则负责封装不需要对用户暴露的上下文方法,比如容器配置的刷新和生命周期相关方法。核心是 refresh 方法负责刷新容器,即容器配置的重新加载、注册和 Bean 的创建。

  1. 应用上下文抽象类实现

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     */
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
        
        @Override
        public void refresh() throws BeansException {
            try {
                // 1. 创建 BeanFactory 并加载BeanDefinition
                refreshBeanFactory();
                ConfigurableListableBeanFactory beanFactory = getBeanFactory();
                // 2. Bean 实例化之前, 调用BeanFactoryPostProcessors
                invokeBeanFactoryPostProcessors(beanFactory);
                // 3. 注册 BeanPostProcessor
                registerBeanPostProcessors(beanFactory);
                // 4. 完成实例化单例Bean
                finishBeanFactoryInitialization(beanFactory);
            } catch (Exception e) {
                throw new BeansException("Application context refresh error", e);
            }
        }
        
        /**
         * Create bean factory, and load bean definitions.
         * @throws BeansException
         */
        protected abstract void refreshBeanFactory() throws BeansException;
        
        public abstract ConfigurableListableBeanFactory getBeanFactory();
        
        /**
         * Instantiate and invoke all registered BeanFactoryPostProcessor beans
         * @param beanFactory
         */
        protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            Map<String, BeanFactoryPostProcessor> beans = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
            for (BeanFactoryPostProcessor postProcessor : beans.values()) {
                postProcessor.postProcessBeanFactory(beanFactory);
            }
        }
        
        protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            Map<String, BeanPostProcessor> beans = beanFactory.getBeansOfType(BeanPostProcessor.class);
            for (BeanPostProcessor beanPostProcessor : beans.values()) {
                beanFactory.addBeanPostProcessor(beanPostProcessor);
            }
        }
        
        protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
            // 完成实例化单例Bean
            beanFactory.preInstantiateSingletons();
        }
        
        @Override
        public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
            return getBeanFactory().getBean(name, requiredType);
        }
      
        ....
    }

依旧是模版方法模式,在 AbstractApplicationContext 里定义了 refresh 方法的流程,包括:

  • 创建一个新的 BeanFactory(先不实现)
  • 执行 BeanFactoryPostProcessor 扩展逻辑(实现)
  • 将 BeanPostProcessor 注册到 BeanFactory 的 beanPostProcessors 列表中;(实现)
  • 执行单例 Bean 的实例化等一系列创建操作;(实现,但核心逻辑由BeanFactory执行)

此外,AbstractApplicationContext 还定义了 refreshBeanFactory()、getBeanFactory() 这些和具体 BeanFactory 相关的操作,交由后续继承者们实现;AbstractApplicationContext 继承了 DefaultResourceLoader,用于在刷新 BeanFactory 时执行资源加载相关的操作;

  1. 创建新的 BeanFactory 并自动“刷新” 执行包括 BeanDefinition 资源加载、和注册的操作;

    public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
        private volatile DefaultListableBeanFactory beanFactory;
        
        @Override
        protected void refreshBeanFactory() throws BeansException {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        }
        
        private DefaultListableBeanFactory createBeanFactory() {
            return new DefaultListableBeanFactory();
        }
        
        /**
         * Load bean definitions into the given bean factory, typically through
         * delegating to one or more bean definition readers.
         *
         * @param beanFactory
         * @throws BeansException
         */
        protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException;
        
        @Override
        public ConfigurableListableBeanFactory  () {
            return this.beanFactory;
        }
        
    }

实现了 refreshBeanFactory 方法,包括:

  • 创建一个 DefaultListableBeanFactory 实例,并实现了getBeanFactory 方法;
  • 定义了 BeanDefinition 的加载方法,交由子类中具体的资源加载器去实现,在其中执行 BeanDefinition 的加载和注册,包括对扩展接口 BeanFactoryPostProcessor 和 BeanPostProcessor 的实现类进行 BeanDefinition 的注册;
  1. 实现对配置信息的加载

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     */
    public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {
        @Override
        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException {
            XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this);
            String[] configLocations = getConfigLocations();
            xmlBeanDefinitionReader.loadBeanDefinitions(configLocations);
        }
        
        /**
         * Get bean definition config locations.
         * @return
         */
        protected abstract String[]  ();
    }

仰仗 BeanDefinitionReader 的接口实现类来执行对配置信息的加载,在 AbstractXmlApplicationContext 中的 loadBeanDefinitions 方法实现中,实例化了一个 XmlBeanDefinitionReader 对象,调用它的 loadBeanDefinitions 方法来执行对特定资源路径的加载;同时,定义了getConfigLocations 方法,子类依据文件的类别来做具体实现;

  1. 应用上下文实现类,通过传入一个资源路径,并自动触发执行应用上下文的全逻辑;

    public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext{
        private String[] configLocations;
        
        public ClassPathXmlApplicationContext(String configLocation) {
            this(new String[]{configLocation});
        }
        
        public ClassPathXmlApplicationContext(String[] configLocations) {
            this.configLocations = configLocations;
            refresh();
        }
        
        @Override
        public String[] getConfigLocations() {
            return this.configLocations;
        }
    }

该实现类中定义了资源路径的属性,并在构造方法中对资源路径进行赋值,然后触发 refresh 方法;

  1. Bean创建时执行初始化的前置、后置逻辑

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Abstract autowire capable bean factory.
     */
    public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
        private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        
        @Override
        public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
            Object bean = null;
            try {
                // 创建Bean实例
                bean = createBeanInstance(beanName, beanDefinition, args);
                // 属性注入
                applyPropertyValues(beanDefinition, beanName, bean);
                // 新增:初始化Bean
                initializeBean(beanName, bean, beanDefinition);
            } catch (Exception e) {
                throw new BeansException("Instantiate bean failed.", e);
            }
            addSingleton(beanName, bean);
            return bean;
        }
        
        /**
         * Instantialize bean.
         *
         * @param beanName
         * @param beanDefinition
         * @param args
         * @return
         * @throws BeansException
         */
        protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
            // Get constructor.
            Class<?> beanClass = beanDefinition.getBeanClass();
            Constructor<?>[] ctors = beanClass.getDeclaredConstructors();
            Object[] argsToUse = null;
            Constructor ctorToUse = null;
            if (null != args) {
                argsToUse = args;
            } else {
                argsToUse = new Object[0];
            }
            for (Constructor<?> ctor : ctors) {
                if (ctor.getParameterCount() == argsToUse.length) {
                    ctorToUse = ctor;
                    break;
                }
            }
            // Instantiation.
            return instantiationStrategy.instantiate(beanDefinition, beanName, ctorToUse, argsToUse);
        }
        
        protected void applyPropertyValues(BeanDefinition beanDefinition, String beanName, Object bean) {
            try {
                PropertyValues propertyValues = beanDefinition.getPropertyValues();
                if (null == propertyValues) {
                    return;
                }
                for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                    String name = propertyValue.getName();
                    Object value = propertyValue.getValue();
                    if (value instanceof BeanReference) {
                        BeanReference beanReference = (BeanReference) propertyValue.getValue();
                        value = getBean(beanReference.getBeanName());
                    }
                    setFieldValue(beanName, bean, name, value);
                }
            } catch (Exception e) {
                throw new BeansException("Apply property values error", e);
            }
        }
        
        protected void setFieldValue(String beanName, Object bean, String propertyName, Object propertyValue) {
            try {
                Class clazz = getBeanDefinition(beanName).getBeanClass();
                Field field = clazz.getDeclaredField(propertyName);
                field.setAccessible(true);
                field.set(bean, propertyValue);
            } catch (NoSuchFieldException | SecurityException e) {
                throw new BeansException("Reflection get field error " + beanName, e);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new BeansException("Reflection set field value error", e);
            }
        }
        
        private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
            // 执行初始化前的自定义扩展方法
            Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
            // 执行初始化方法
            invokeInitMethods(beanName, bean, beanDefinition);
            // 执行初始化后的自定义扩展方法
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            return wrappedBean;
        }
        
        protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws BeansException {
        
        }
        
        @Override
        public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {
            Object result = existingBean;
            for (BeanPostProcessor postProcessor : getBeanPostProcessors()) {
                Object current = postProcessor.postProcessBeanBeforeInitialization(result, beanName);
                if (current == null) {
                    return result;
                }
                result = current;
            }
            return result;
        }
        
        @Override
        public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) {
            Object result = existingBean;
            for (BeanPostProcessor postProcessor : getBeanPostProcessors()) {
                Object current = postProcessor.postProcessBeanAfterInitialization(result, beanName);
                if (current == null) {
                    return result;
                }
                result = current;
            }
            return result;
        }
    }

在 Spring 中,Bean创建包括三个步骤:实例化、属性注入和初始化;

实例化和初始化的区别

实例化」:实例化是创建类的实例的过程。在Spring中,当一个Bean被定义在配置文件中(或者通过其他方式如注解或Java配置),Spring IoC容器就会实例化这个Bean。实例化通常通过调用类的无参数构造函数来完成。这个步骤产生了一个Bean的实例,但是这个实例的属性尚未被设置。

初始化」:初始化是在Bean实例化后、使用前的一个阶段,主要是对Bean进行一些定制化的设置,比如设置属性的值、执行某些方法等。在Spring中,你可以通过实现InitializingBean接口,或者使用@PostConstruct注解,或者在XML配置中定义<bean init-method="">,来定义Bean的初始化逻辑(后面我们会解释)。

那么我们自定义的 BeanPostProcessor 就是在 Bean 实例化和属性注入之后,在初始化之前和之后分别执行 applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization 方法,这两个方法定义在 AutowireCapableBeanFactory 中;

以 applyBeanPostProcessorsBeforeInitialization 为例:具体执行逻辑为把 BeanFactory 中注册到 beanPostProcessors 列表里的 BeanPostProcessor 实例,执行 postProcessBeanBeforeInitialization 方法,直到返回为空,这时候扩展逻辑执行结束,返回 wrappedBean 即可。

一些总结

到这里为止,我们可以发现在 Spring 中,接口、抽象类的职责和层次划分非常清晰,每层继承后只增加了“原子”的功能,提供了高度自由的能力组合,最终落到实现类身上供用户使用。一个应用上下文主要包含了三个职责:Bean 工厂的创建和配置读取、Bean 的创建、以及将全流程串起来自动执行的模版方法;

3 初始化和销毁方法的扩展

分支:github.com/shengdoupi/…

前面我们在 Bean 实例化和属性注入之后有一个初始化的过程,这个初始化方法前后我们也留了扩展点(BeanPostProcessor). 那么 Bean 的初始化到底是什么呢?

Bean 初始化:我们希望在 Bean的实例化、属性注入这些基本创建流程完成后,但在 Bean 被获取使用之前,执行一些逻辑;比如,当我们一个缓存客户端类的 Bean 被实例化之后,我们希望在使用这个客户端之前,一些缓存已经被加载,这时我们可以在这个类中定义一个加载缓存的方法,通过在初始化方法中执行该方法来实现缓存的提前加载;

因此,初始化方法本质上也是一个 Bean工厂 提供的扩展点,框架需要定义接口来实现扩展点的执行,使用者通过实现接口来定义具体的执行逻辑;

Bean 的销毁也一样,框架通过接口作为扩展点,使用者实现接口来定义销毁时需要执行的逻辑,大多数为资源释放操作;

Spring 中,初始化和销毁方法扩展点有两种,一种是上面说的接口调用,另一种是在 BeanDefinition 中注册具体方法名称,然后在初始化过程中通过反射执行该方法;初始化方法的扩展点埋在 Bean 创建流程中;而销毁方法的扩展点则通过向虚拟机注册关闭勾子进行回调;

  1. 定义初始化和销毁方法的接口

    package io.github.shengdoupi.springframework.beans.factory;

    /**
     * @author zhoukehh
     * @date 2024/4/30
     * @description Interface to be implemented by beans that need react once all properties has been set.
     */
    public interface InitializingBean {
        
        /**
         * Invoked by bean factory after properties set.
         * @throws Exception
         */
        void afterPropertiesSet() throws Exception;
        
    }

    package io.github.shengdoupi.springframework.beans.factory;

    /**
     * @author zhoukehh
     * @date 2024/4/30
     * @description Interface to be implemented by beans that need to release resources on destruction.
     */
    public interface DisposableBean {
        
        /**
         * Invoked by bean factory on destruction of a bean.
         * @throws Exception
         */
        void destroy() throws Exception;
        
    }

当实现了这两个接口的类以 Bean 的形式注册到 BeanFactory 时,正如之前所说,分别会在 Bean 的属性注入完成后调用接口方法执行初始化操作,在 Bean 被虚拟机销毁时调用接口方法执行销毁操作;

  1. BeanDefinition 增加初始化方法和销毁方法属性

    package io.github.shengdoupi.springframework.beans.factory.support;
    ...

    /**
     * @author zhoukehh
     * @date 2024/4/20
     * @description Xml bean definition reader.
     */
    public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
        public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
            super(registry);
        }
        
        public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
            super(registry, resourceLoader);
        }
        
        @Override
        public void loadBeanDefinitions(Resource resource) throws BeansException {
            try {
                doLoadBeanDefinitions(resource.getInputStream(), resource);
            } catch (IOException e) {
                throw new BeansException("Get input stream error", e);
            }
        }
       ...
        
        
        private void doLoadBeanDefinitions(InputStream inputStream, Resource resource) {
            try {
                // parse doc
                Document doc = XMLUtils.read(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;
                    }
                    // parse element
                    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 destroyMethod = bean.getAttribute("destroy-method");
                    // get clazz
                    Class<?> clazz = Class.forName(className);
                    // beanName
                    String beanName = StringUtils.isNotBlank(id) ? id : name;
                    if (StringUtils.isBlank(beanName)) {
                        beanName = clazz.getSimpleName();
                    }
                    // BeanDefinition
                    BeanDefinition beanDefinition = new BeanDefinition(clazz);
                    NodeList beanChildNodes = bean.getChildNodes();
                    PropertyValues propertyValues = new PropertyValues();
                    beanDefinition.setPropertyValues(propertyValues);
                    beanDefinition.setInitMethodName(initMethod);
                    beanDefinition.setDestroyMethodName(destroyMethod);
                    ...
            } catch (Exception e) {
                throw new BeansException("Load bean definition error", e);
            }
        }
    }

XmlBeanDefinitionReader 将配置的初始化和销毁方法名称放到 BeanDefinition 中,后面通过反射调用执行;

  1. 初始化方法的执行

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Abstract autowire capable bean factory.
     */
    public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
        private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        
        @Override
        public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
            Object bean = null;
            try {
                // 创建Bean实例
                bean = createBeanInstance(beanName, beanDefinition, args);
                // 属性注入
                applyPropertyValues(beanDefinition, beanName, bean);
                // 初始化Bean
                initializeBean(beanName, bean, beanDefinition);
            } catch (Exception e) {
                throw new BeansException("Instantiate bean failed.", e);
            }
            addSingleton(beanName, bean);
            return bean;
        }
      
      ...
        
        private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
            // 执行初始化前的自定义方法
            Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
            // 执行初始化方法
            invokeInitMethods(beanName, bean, beanDefinition);
            // 执行初始化后的自定义方法
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            return wrappedBean;
        }
        
        protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws BeansException {
            try {
                if (bean instanceof InitializingBean) {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return;
                }
                if (StringUtils.isBlank(beanDefinition.getInitMethodName())) {
                    return;
                }
                String initMethodName = beanDefinition.getInitMethodName();
                Class clazz = beanDefinition.getBeanClass();
                Method initMethod = clazz.getDeclaredMethod(initMethodName);
                if (Objects.isNull(initMethod)) {
                    throw new Exception("Init method:" + initMethodName + "can not find error.");
                }
                initMethod.invoke(bean);
            } catch (Exception e) {
                throw new BeansException("Init methods invoke error.", e);
            }
            
        }
        
        ...
    }

在属性注入之后,执行 invokeInitMethods 方法,执行接口调用或者反射调用;

  1. Bean 销毁适配器

    ckage io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/30
     * @description
     */
    public class DisposableBeanAdapter implements DisposableBean {
        
        private final Object bean;
        
        private final String beanName;
        
        private final 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 {
            try {
                if (bean instanceof DisposableBean) {
                    ((DisposableBean) bean).destroy();
                    return;
                }
                if (StringUtils.isNotBlank(destroyMethodName)
                        && !(bean instanceof DisposableBean && "destroy".equals(destroyMethodName)) {
                    Method destroyMethod = this.bean.getClass().getMethod(destroyMethodName);
                    if (Objects.isNull(destroyMethod)) {
                        throw new Exception("Destroy method null error.");
                    }
                    destroyMethod.invoke(bean);
                }
                
            } catch (Exception e) {
                throw new BeansException("Destroy bean error.", e);
            }
        }
    }

使用适配器进行包装的原因:一个 Bean 可以继承多个接口实现不同的方法来执行销毁逻辑,比如继承 DisposableBean 实现 destroy() 方法,继承 AutoCloseable 接口实现 close() 方法等;而对 Bean 工厂框架来说,在 Bean 的创建流程中,只想通过一个入口调用就去触发销毁逻辑,而不想感知具体的销毁方法是继承自DisposableBean 实现的 destroy() 方法,还是继承 AutoCloseable 接口实现 close() 方法等;因此,使用一个适配器将所有可能的销毁方法调用都用一个方法包装起来,供给 Spring 交互;通过适配器模式使得框架的分层更加合理,代码更加容易理解;

  1. 创建 Bean 时注册销毁适配器对象

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Abstract autowire capable bean factory.
     */
    public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
        private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        
        @Override
        public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
            Object bean = null;
            try {
                // 创建Bean实例
                bean = createBeanInstance(beanName, beanDefinition, args);
                // 属性注入
                applyPropertyValues(beanDefinition, beanName, bean);
                // 初始化Bean
                initializeBean(beanName, bean, beanDefinition);
            } catch (Exception e) {
                throw new BeansException("Instantiate bean failed.", e);
            }
            //
            registerDisposableBeanIfNecessary(bean, beanDefinition, beanName);
            addSingleton(beanName, bean);
            return bean;
        }
        
        ...
        
        private void registerDisposableBeanIfNecessary(Object bean, BeanDefinition beanDefinition, String beanName) {
            if (null == bean) {
                return;
            }
            if (bean instanceof DisposableBean || StringUtils.isNotBlank(beanDefinition.getDestroyMethodName())) {
                registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
            }
        }
    }

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Generic registry for shared bean instances.
     * Also supports registration for DisposableBean instances, dependencies of beans can be registered
     * to enforce an appropriate shutdown order.
     */
    public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
        Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
        
        Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();
      
        ...
      
        public void registerDisposableBean(String beanName, DisposableBean bean) {
            disposableBeans.put(beanName, bean);
        }
    }

需要执行销毁逻辑的 Bean 需要在创建时就注册到 disposableBeans 中,用于后面程序关闭时统一调用;

Bean 工厂的 DisposableBean 注册表是在 DefaultSingletonBeanRegistry 中,如果是单例非懒加载的 Bean,在初始化后注册 DisposableBeanAdapter 对象到注册表中。通过适配器包装后,无论销毁逻辑是怎么实现的,都可以交给适配器去调用执行;

可以关注到 DisposableBean 注册表是由 LinkedHashMap 实现,Spring 注释中提到这样是让注册顺序得到保留,在销毁时按照该顺序进行调用执行销毁方法,使得拥有相互依赖的 Bean 之间的销毁顺序可以得到保证;

  1. 销毁方法的执行

    package io.github.shengdoupi.springframework.beans.factory.config;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/26
     * @description Configurable bean factory, provides facilities to configure a bean factory.
     */
    public interface ConfigurableBeanFactory extends BeanFactory {
        
       ...
        
        /**
         * Destroy all singleton beans in this bean factory, include inner beans that registered as disposable bean.
         */
        void destroySingletons();
    }

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Default listable bean factory.
     */
    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
            implements BeanDefinitionRegistry, ConfigurableListableBeanFactory {
        
        ...
        
        @Override
        public void destroySingletons() {
            super.destroySingletons();
        }
    }

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Generic registry for shared bean instances.
     * Also supports registration for DisposableBean instances, dependencies of beans can be registered
     * to enforce an appropriate shutdown order.
     */
    public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
        
        ...
      
        public void destroySingletons() {
            try {
                for (String beanName : disposableBeans.keySet()) {
                    DisposableBean bean = disposableBeans.remove(beanName);
                    bean.destroy();
                }
            } catch (Exception e) {
                throw new BeansException("Destroy singletons error", e);
            }
        }
    }

Bean 工厂需要提供一个统一的销毁入口,供虚拟机关闭钩子调用;该入口定义在 ConfigurableBeanFactory 中;

BeanFactory 的实现类自身不负责实现这个统一销毁逻辑,而是委托给其父类 DefaultSingletonBeanRegistry 执行;这里的设计模式:父类1的方法不由子类具体实现,而是子类委托给另一个和父类1同层的父类2去实现,主要体现了功能分层的思想。

    package io.github.shengdoupi.springframework.context.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     */
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
      
        ...
        
        @Override
        public void registerShutDownHook() {
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                @Override
                public void run() {
                    close();
                }
            }));
        }
        
        @Override
        public void close() {
            getBeanFactory().destroySingletons();
        }
    }

最终,在应用上下文中向虚拟机关闭钩子注册该统一销毁入口;注册钩子时参数为 Thread 对象;

注意到这里 refresh 方法中并没有调用 registerShutDownHook 方法,因此在使用应用上下文时需要手动调用一次该方法;

4 反向扩展 -- Bean 感知 Spring 的能力

分支:github.com/shengdoupi/…

Bean 是由 Spring 管理的,Bean 对象在整个生命周期中都被 Spring 的各个模块所使用。那么如果我们需要 Bean 在生命周期的特定时刻可以使用 Spring 自身的资源,即管理它的资源,比如 ResourceLoader、BeanFactory、ApplicationContext 等,该如何实现呢?

这种情况还是存在的,比如我们想要一个 Bean 在创建的时候进行资源的加载,这时候我们可以利用到 Spring 的 ResourceLoader;或者我们想要一个 Bean 在创建的过程中使用另一个 Bean 执行一些操作,这时候可以利用到 Spring 的 BeanFactory;

Spring 提供了 Aware(感知)机制。具体地,Aware 接口是一组标记接口,具体的子类定义和实现能够感知对应的框架组件对象,用于在 Bean 的生命周期中获取到 Spring 的组件资源,去处理复杂的业务逻辑;

  1. 定义 Aware 接口

    package io.github.shengdoupi.springframework.beans.factory;

    /**
     * @author zhoukehh
     * @date 2024/5/2
     * @description A marker interface indicating that a bean is eligible to be notified
     * by Spring container of a specific framework object through a callback-style method.
     */
    public interface Aware {
    }

一个标记接口表明一个 Bean 会通过容器回调的方式,感知到框架中的某个组件对象;

  1. 具体组件感知类(接口)

    package io.github.shengdoupi.springframework.beans.factory;

    import io.github.shengdoupi.springframework.beans.BeansException;

    /**
     * @author zhoukehh
     * @date 2024/5/2
     * @description Interface to be implemented by beans that wish to be aware of their owner -- BeanFactory.
     */
    public interface BeanFactoryAware extends Aware{
        
        /**
         * Callback that supplies the owning bean factory to a bean instance.
         * Invoked after properties population but before an init callback such as
         * afterPropertiesSet() or a custom init-method.
         * @param beanFactory
         * @throws BeansException
         */
        void setBeanFactory(BeanFactory beanFactory) throws BeansException;
    }

想要感知 BeanFactory 的 Bean 需要实现该接口;

    package io.github.shengdoupi.springframework.beans.factory;

    import io.github.shengdoupi.springframework.beans.BeansException;

    /**
     * @author zhoukehh
     * @date 2024/5/2
     * @description Callback that allows a bean to be aware of the bean class loader
     * -- that the bean factory used to load bean classes.
     */
    public interface BeanClassLoaderAware extends Aware {
        
        /**
         * Callback that supplies the bean class loader to a bean instance.
         * Invoked after properties population but before an init callback such as
         * afterPropertiesSet() or a custom init-method.
         * @param classLoader
         * @throws BeansException
         */
        void setBeanClassLoader(ClassLoader classLoader) throws BeansException;
    }

如果 Bean 需要感知 BeanFactory 用于加载 Bean class 的 ClassLoader 对象,则实现该接口;

    package io.github.shengdoupi.springframework.beans.factory;

    import io.github.shengdoupi.springframework.beans.BeansException;

    /**
     * @author zhoukehh
     * @date 2024/5/2
     * @description Interfaces to be implemented by beans that wish to be aware of their
     * bean name in this bean factory.
     */
    public interface BeanNameAware extends Aware{
        
        /**
         * Set the name of the bean in the bean factory that created this bean.
         * Invoked after properties population but before an init callback such as
         * afterPropertiesSet() or a custom init-method.
         * @param beanName
         * @throws BeansException
         */
        void setBeanName(String beanName) throws BeansException;
    }

如果 Bean 需要感知自己在容器中的名称,则实现该接口;

    package io.github.shengdoupi.springframework.context;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/5/2
     * @description Interface to be implemented by a object that want to be notified of the application context it runs in.
     */
    public interface ApplicationContextAware extends Aware {
        
        /**
         * Set the ApplicationContext that this object runs in.
         * Normally this call will be used to initialize the object.
         * Invoked after properties population but before an init callback such as
         * afterPropertiesSet() or a custom init-method. Invoked after ResourceLoadedAware.
         * @param applicationContext
         * @throws BeansException
         */
        void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
    }

如果 Bean 需要感知自己所在的应用上下文,则实现该接口;

  1. ApplicationContextAware 处理器

    package io.github.shengdoupi.springframework.context.support;

    import ...
    /**
     * @author zhoukehh
     * @date 2024/5/2
     * @description BeanPostProcessor implementation that supplies the ApplicationContext
     * to beans that implement the ApplicationContextAware interface.
     */
    public class ApplicationContextAwareProcessor implements BeanPostProcessor {
        
        private final ApplicationContext applicationContext;
        
        /**
         * Create a ApplicationContextAwareProcessor for a given context.
         * @param applicationContext
         */
        public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
        
        @Override
        public Object postProcessBeanBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(applicationContext);
            }
            return bean;
        }
        
        /**
         * Modify a bean after its initialization.
         * @param bean
         * @param beanName
         * @return The bean instance to use, if null, no subsequent BeanPostProcessors will be invoked.
         * @throws BeansException
         */
        @Override
        public Object postProcessBeanAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }

由于 ApplicationContext 无法从 BeanFactory 中直接拿到,因此需要想额外办法让 Bean 能感知到 ApplicationContext;

Spring 通过 BeanPostProcesoor 来让 Bean 在创建时拿到 ApplicationContext,具体地,框架内部定义一个类来实现 BeanPostProcesoor,该类在应用启动时(即 refresh() 时)初始化一个对象,并将此时的应用上下文对象赋予到该类的上下文属性;而该类的 postProcessBeanBeforeInitialization 方法就是把其上下文属性对象赋予实现了 ApplicationContextAware 接口的 Bean;

显然,作为一个 BeanPostProcessor 实现类,ApplicationContextAwareProcessor 需要在合适的时机注册到 BeanFactory 的 beanPostProcessors 注册表中;

  1. ApplicationContextAwareProcessor 注册

    package io.github.shengdoupi.springframework.context.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     */
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
        
        @Override
        public void refresh() throws BeansException {
            try {
                // 1. 创建 BeanFactory 并加载BeanDefinition
                refreshBeanFactory();
                ConfigurableListableBeanFactory beanFactory = getBeanFactory();
                // 添加 ApplicationContextAwareProcessor 到 BeanFactory 注册表中
                beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
                // 2. Bean 实例化之前, 调用BeanFactoryPostProcessors
                invokeBeanFactoryPostProcessors(beanFactory);
                // 3. 注册 BeanPostProcessor
                registerBeanPostProcessors(beanFactory);
                // 4. 完成实例化单例Bean
                finishBeanFactoryInitialization(beanFactory);
            } catch (Exception e) {
                throw new BeansException("Application context refresh error", e);
            }
        }
      
        ...
    }

如前所说,ApplicationContextAwareProcessor 需要在合适的时机注册到 BeanFactory 的 beanPostProcessors 注册表中,这个时机就是在应用上下文启动时,BeanFactory 实例化之后;

  1. Bean 创建过程中感知回调

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Abstract autowire capable bean factory.
     */
    public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
        private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        
        @Override
        public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
            Object bean = null;
            try {
                // 创建Bean实例
                bean = createBeanInstance(beanName, beanDefinition, args);
                // 属性注入
                applyPropertyValues(beanDefinition, beanName, bean);
                // 初始化Bean
                initializeBean(beanName, bean, beanDefinition);
            } catch (Exception e) {
                throw new BeansException("Instantiate bean failed.", e);
            }
            //
            registerDisposableBeanIfNecessary(bean, beanDefinition, beanName);
            addSingleton(beanName, bean);
            return bean;
        }
      
        ...
      
        private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
            
            if (bean instanceof Aware) {
                if (bean instanceof BeanNameAware) {
                    ((BeanNameAware) bean).setBeanName(beanName);
                }
                if (bean instanceof BeanFactoryAware) {
                    ((BeanFactoryAware) bean).setBeanFactory(this);
                }
                if (bean instanceof BeanClassLoaderAware) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(super.getClassLoader());
                }
            }
            
            // 执行初始化前的自定义方法
            Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
            // 执行初始化方法
            invokeInitMethods(beanName, bean, beanDefinition);
            // 执行初始化后的自定义方法
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            return wrappedBean;
        }
        
       ...
        
        @Override
        public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {
            Object result = existingBean;
            for (BeanPostProcessor postProcessor : getBeanPostProcessors()) {
                Object current = postProcessor.postProcessBeanBeforeInitialization(result, beanName);
                if (current == null) {
                    return result;
                }
                result = current;
            }
            return result;
        }
        
        ...
    }

在合适的时机(Bean 属性注入之后,初始化之前)执行感知回调;其中,ApplicationContextAware 会在 BeanPostProcessor 执行时进行回调。

转存失败,建议直接上传图片文件

5 Bean 对象作用域

分支:github.com/shengdoupi/…

前面 Bean 的创建和获取的逻辑都只处理了单例 Bean :创建时放入单例 Bean 注册表中,获取时从内存中获取;

有时我们需要 Bean 工厂提供原型 Bean 的获取能力,即每次获取到的都是新创建的一个对象,而不是共享对象;这种场景很常见,比如 Bean 包含某个有状态属性,每次执行逻辑都需要重置状态,那么自然也就需要重新创建一个对象;

单例 (Singleton) 和原型 (Prototype) 即是 Bean 的两种作用域,这一部分我们会在 Bean 的创建和获取过程增加对原型作用域的处理逻辑,具体地:

  • Bean 作用域需要在 BeanDefinition 中就指定好,即需要在 BeanDefinition 中新增作用域相关属性;
  • XML 配置新增 scope 字段,配置读取时将 scope 加载到 BeanDefinition ;
  • 创建 Bean 时根据  BeanDefinition 中的作用域来决定是否放入单例 Bean 注册表;
  1. BeanDefinition 新增作用域属性

    package io.github.shengdoupi.springframework.beans.factory.config;

    import io.github.shengdoupi.springframework.beans.PropertyValues;

    /**
     * @author zhoukehh
     * @date 2024/3/21
     * @description Bean definition.
     */
    public class BeanDefinition {
        /**
         * Scope identifier for the standard singleton scope
         */
        String SCOPE_SINGLETON = "singleton";
        
        /**
         * Scope identifier for the standard prototype scope
         */
        String SCOPE_PROTOTYPE = "prototype";
        
        private Class beanClass;
        
        private String initMethodName;
        
        private String destroyMethodName;
        
        private PropertyValues propertyValues;
        
        private String scope = SCOPE_SINGLETON;
        
        private boolean singleton = true;
        
        private boolean prototype = false;
        
        public BeanDefinition(Class beanClass) {
            this.beanClass = beanClass;
        }
        
        public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
            this.beanClass = beanClass;
            this.propertyValues = propertyValues;
        }
      
        public void setScope(String scope) {
            this.scope = scope;
            this.singleton = SCOPE_SINGLETON.equals(scope);
            this.prototype = SCOPE_PROTOTYPE.equals(scope);
        }
        
        
       ...
    }
  1. XML 配置新增 scope 字段,并读入 BeanDefinition

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/20
     * @description Xml bean definition reader.
     */
    public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
        ...
        
        private void doLoadBeanDefinitions(InputStream inputStream, Resource resource) {
            try {
                // parse doc
                Document doc = XMLUtils.read(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;
                    }
                    // parse element
                    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 destroyMethod = bean.getAttribute("destroy-method");
                    String scope = bean.getAttribute("scope");
                    // get clazz
                    Class<?> clazz = Class.forName(className);
                    // beanName
                    String beanName = StringUtils.isNotBlank(id) ? id : name;
                    if (StringUtils.isBlank(beanName)) {
                        beanName = clazz.getSimpleName();
                    }
                    // BeanDefinition
                    BeanDefinition beanDefinition = new BeanDefinition(clazz);
                    NodeList beanChildNodes = bean.getChildNodes();
                    PropertyValues propertyValues = new PropertyValues();
                    beanDefinition.setPropertyValues(propertyValues);
                    beanDefinition.setInitMethodName(initMethod);
                    beanDefinition.setDestroyMethodName(destroyMethod);
                    for (int j = 0; j < beanChildNodes.getLength(); ++j) {
                        if (!(beanChildNodes.item(j) instanceof Element)) {
                            continue;
                        }
                        if (!"property".equals(beanChildNodes.item(j).getNodeName())) {
                            continue;
                        }
                        // parse property
                        Element property = (Element) beanChildNodes.item(j);
                        String attrName = property.getAttribute("name");
                        String attrValue = property.getAttribute("value");
                        String attrRef = property.getAttribute("ref");
                        Object value = StringUtils.isNotBlank(attrRef) ? (BeanReference) () -> attrRef : attrValue;
                        propertyValues.addPropertyValue(new PropertyValue(attrName, value));
                    }
                    if (StringUtils.isNotBlank(scope)) {
                        beanDefinition.setScope(scope);
                    }
                    if (getRegistry().containsBeanDefinition(beanName)) {
                        throw new BeansException("Duplicate bean name: " + beanName);
                    }
                    // Register bean definition.
                    getRegistry().registerBeanDefinition(beanName, beanDefinition);
                }
            } catch (Exception e) {
                throw new BeansException("Load bean definition error", e);
            }
            
        }
    }
  1. 创建 Bean 时依据作用域决定是否放入单例 Bean 注册表

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description Abstract autowire capable bean factory.
     */
    public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
        private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        
        @Override
        public Object creatBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
            Object bean = null;
            try {
                // 创建Bean实例
                bean = createBeanInstance(beanName, beanDefinition, args);
                // 属性注入
                applyPropertyValues(beanDefinition, beanName, bean);
                // 初始化Bean
                initializeBean(beanName, bean, beanDefinition);
            } catch (Exception e) {
                throw new BeansException("Instantiate bean failed.", e);
            }
            //
            registerDisposableBeanIfNecessary(bean, beanDefinition, beanName);
            if (beanDefinition.isSingleton()) {
                addSingleton(beanName, bean);
            }
            return bean;
        }

        private void registerDisposableBeanIfNecessary(Object bean, BeanDefinition beanDefinition, String beanName) {
              if (null == bean) {
                  return;
              }
              if (!beanDefinition.isSingleton()) {
                  return;
              }
              if (bean instanceof DisposableBean || StringUtils.isNotBlank(beanDefinition.getDestroyMethodName())) {
                  registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
              }
          }
      
        ...
        
    }

非单例 Bean 不需要执行销毁方法;

6 创建复杂 Bean -- FactoryBean 扩展

分支:github.com/shengdoupi/…

一个 Bean 所包含的信息都被注册在 BeanDefinition 中,而 BeanDefinition 由使用者配置后被框架读取加载得到;目前 simple-spring 中一个 BeanDefinition 可以配置的信息包括:

  • Bean 对象所属的 Class 类;
  • Bean 对象的属性名称和属性值,包括是 Bean 对象的属性;
  • 初始化和销毁方法;
  • 对象作用域,单例还是原型等;

在某些情况下,创建 Bean 的过程相当复杂,这时需要在 BeanDefinition 配置中提供大量的配置信息,这时配置的方法对上层(使用者)来说显得不够友好,如果能采用编码的方法效果会更好;

Spring 中提供了一个 FactoryBean 接口,使用者通过实现该接口定制化创建 Bean 的逻辑;

FactoryBean 一般用来创建复杂的 Bean,如果一个 Bean 在创建的过程中涉及到很多其他的 Bean 或者复杂逻辑,用 XML 配置会比较困难,这时可以考虑用 FactoryBean;

  1. 定义 FactoryBean 接口

    package io.github.shengdoupi.springframework.beans.factory;

    import io.github.shengdoupi.springframework.beans.BeansException;

    /**
     * @author zhoukehh
     * @date 2024/5/5
     * @description Interface to be implemented by objects used within a BeanFactory which
     * are themselves factories for individual objects.
     * If a bean implements this interface, it is used as a factory for an object to expose,
     * not directly as an instance that will be exposed itself;
     */
    public interface FactoryBean {
        
        /**
         * Return an instance of the object managed by this factory;
         * @return
         * @throws BeansException
         */
        Object getObject() throws BeansException;
        
        /**
         * Return the type of object that this FactoryBean creates;
         * @return
         * @throws BeansException
         */
        Class getObjectType() throws BeansException;
        
        /**
         * Return if the object managed by this FactoryBean is singleton.
         * @return
         */
        default boolean isSingleton() {
            return true;
        }
    }

接口提供三个方法,获取该 factory 管理的 bean 对象、其类型以及是否单例;

  1. FactoryBean 注册服务

    package io.github.shengdoupi.springframework.beans.factory.support;

    import io.github.shengdoupi.springframework.beans.BeansException;
    import io.github.shengdoupi.springframework.beans.factory.FactoryBean;

    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;

    /**
     * @author zhoukehh
     * @date 2024/5/5
     * @description Support base class for singleton registries which need to handle FactoryBean instances.
     */
    public class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry{
        
        /**
         * Cache of singleton object created by factory beans: FactoryBean name to object.
         */
        private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
        
        /**
         * Determine the type for the given FactoryBean.
         * @param factoryBean
         * @return
         */
        protected Class<?> getClassForFactoryBean(FactoryBean factoryBean) {
            try {
                return factoryBean.getObjectType();
            } catch (Exception e) {
                throw new BeansException("Get clas for factory bean error", e);
            }
        }
        
        /**
         * Obtain an object to expose from the given FactoryBean, if cached in factoryBeanObjectCache.
         * @param factoryBeanName
         * @return
         */
        protected Object getCachedObjectForFactoryBean(String factoryBeanName) {
            return this.factoryBeanObjectCache.get(factoryBeanName);
        }
        
        /**
         * Obtain an object to expose from the given FactoryBean.
         * @param beanName
         * @param factory
         * @return
         */
        protected Object getObjectFromFactoryBean(String beanName, FactoryBean factory) {
            Object object;
            if (factory.isSingleton()) {
                object = this.factoryBeanObjectCache.get(beanName);
                if (null == object) {
                    object = doGetObjectFromFactoryBean(factory, beanName);
                    this.factoryBeanObjectCache.put(beanName, object);
                }
            } else {
                object = doGetObjectFromFactoryBean(factory, beanName);
            }
            return object;
        }
        
        /**
         * Obtain an object to expose from the given FactoryBean.
         * @param factory
         * @param beanName
         * @return
         */
        private Object doGetObjectFromFactoryBean(FactoryBean factory, String beanName) {
            Object object;
            try {
                object = factory.getObject();
            } catch (Exception e) {
                throw new BeansException("Get object from factory bean error, beanName: " + beanName, e);
            }
            return object;
        }
    }

我们知道 FactoryBean 类的工厂对象getObject获取的是 FactoryBean 工厂管理的对象,Spring 也支持这类对象的作用域管理,如果是单例还需要有单例对象注册表缓存;

FactoryBeanRegistrySupport 用来获取 FactoryBean 类的工厂对象所管理的对象的获取,同时继承了 DefaultSingletonBeanRegistry ,方便 Bean 工厂只需要继承 FactoryBeanRegistrySupport 类,即可具备对普通 Bean 对象、 FactoryBean 工厂对象以及该工厂对象所管理的具体对象,三种 Bean 的注册能力;

  1. 扩展 BeanFactory 获取 FactoryBean 逻辑

    package io.github.shengdoupi.springframework.beans.factory.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/3/26
     * @description
     */
    public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport
            implements BeanFactory, ConfigurableBeanFactory {
        
        ...
        
        @Override
        public Object getBean(String beanName) throws BeansException {
            return doGetBean(beanName, null, null);
        }
        
        protected <T> T doGetBean(String beanName, Class<T> requiredType, Object... args) {
            Object bean = null;
            bean = getSingleton(beanName);
            if (bean == null) {
                BeanDefinition beanDefinition = getBeanDefinition(beanName);
                bean = creatBean(beanName, beanDefinition, args);
            }
            return getObjectForBeanInstance(beanName, bean, requiredType);
        }
        
        protected <T> T getObjectForBeanInstance(String beanName, Object bean, Class<T> requiredType) {
            if (!(bean instanceof FactoryBean)) {
                return adaptBeanInstance(beanName, bean, requiredType);
            }
            Object object = getCachedObjectForFactoryBean(beanName);
            if (null == object) {
                FactoryBean<?> factoryBean = (FactoryBean<?>) bean;
                object = getObjectFromFactoryBean(beanName, factoryBean);
            }
            return (T) object;
        }
        
        ...
        
        
    }

对于 getBean() 方法做针对 FactoryBean 的扩展逻辑:首先和普通 Bean 对象一样,获取到 FactoryBean 工厂对象;然后判断创建出来的对象若是 FactoryBean 工厂对象,将该对象替换为  FactoryBean 工厂对象所管理的对象;

7 容器事件机制

分支:github.com/shengdoupi/…

Spring 提供了容器事件机制,具体的,通过实现容器事件接口定义事件类型,向容器中注册事件的监听者,最后通过容器(应用上下文)发布事件,容器将回调注册了的该事件监听者们,执行具体逻辑;

事件机制的角色:

  • 事件源
  • 事件监听者注册表
  • 事件广播器

Spring 基于观察者模式,分别就这三个身份对事件机制做了设计和实现。观察者模式的核心在于抽象依赖抽象,事件机制中的事件源和事件监听者均被做了抽象而相互依赖,事件广播器则是依赖于抽象的事件源、抽象的事件监听者,是一种控制反转思想;

  1. 定义事件抽象

    package io.github.shengdoupi.springframework.context;

    import java.util.EventObject;

    /**
     * @author zhoukehh
     * @date 2024/5/7
     * @description Class to be extended by all application events.
     */
    public abstract class ApplicationEvent extends EventObject {
        /**
         * Constructs a prototypical Event.
         *
         * @param source The object on which the Event initially occurred.
         * @throws IllegalArgumentException if source is null.
         */
        public ApplicationEvent(Object source) {
            super(source);
        }
    }

所有容器事件的抽象类,包括用户自定义的事件都是该类的具体实现。

包含一个 source 属性,代表发生事件的对象(事件发生地);

  1. 定义抽象监听者

    package io.github.shengdoupi.springframework.context;

    import java.util.EventListener;

    /**
     * @author zhoukehh
     * @date 2024/5/7
     * @description Interface to be implemented by all application listeners.
     * Based on the standard interface @EventListener for the Observer design pattern.
     */
    public interface ApplicationListener <E extends ApplicationEvent> extends EventListener {
        
        /**
         * Handle an application event.
         * @param event
         */
        void onApplicationEvent(E event);
    }

事件监听者只需监听自己关心的事件源;

  1. 定义广播器接口

    package io.github.shengdoupi.springframework.context.event;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description Interface to be implemented by objects that can register and manage ApplicationListener objects
     * and publish events to them.
     */
    public interface ApplicationEventMulticaster {
        
        /**
         * Add a listener to be notified of all events.
         * @param applicationListener
         */
        void addApplicationListener(ApplicationListener<?> applicationListener);
        
        /**
         * Remove a listener from the notification list.
         * @param applicationListener
         */
        void removeApplicationListener(ApplicationListener<?> applicationListener);
        
        /**
         * Multicast the given application event to appropriate listeners.
         * @param applicationEvent
         */
        void multicastEvent(ApplicationEvent applicationEvent);
    }

广播器的作用主要是注册、管理和通知监听者,当事件发生时,需要调用一次广播器;

  1. 实现广播器

    package io.github.shengdoupi.springframework.context.event;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description
     */
    public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
        
        public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new HashSet<>();
        
        private BeanFactory beanFactory;
        
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }
        
        @Override
        public void addApplicationListener(ApplicationListener<?> applicationListener) {
            applicationListeners.add((ApplicationListener<ApplicationEvent>) applicationListener);
        }
        
        @Override
        public void removeApplicationListener(ApplicationListener<?> applicationListener) {
            applicationListeners.remove(applicationListener);
        }
        
        protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
            List<ApplicationListener> allListeners = new LinkedList<>();
            for (ApplicationListener applicationListener : applicationListeners) {
                if (supportEvent(applicationListener, event)) {
                    allListeners.add(applicationListener);
                }
            }
            return allListeners;
        }
        
        protected boolean supportEvent(ApplicationListener listener, ApplicationEvent event) {
            Type type = listener.getClass().getGenericInterfaces()[0];
            Type actualTypeAugment = ((ParameterizedType) type).getActualTypeArguments()[0];
            String className = actualTypeAugment.getTypeName();
            Class<?> eventClassname;
            try {
                eventClassname = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeansException("Wrong event class name: " + className);
            }
            // A.isAssignableFrom(B) -> A 是 B 的父类
            return eventClassname.isAssignableFrom(event.getClass());
        }
    }

AbstractApplicationEventMulticaster 主要是实现广播器的基本功能: 监听器的注册和移除,以及获取对某个事件感兴趣的监听器集合;

主要是通过获取监听者父类 ApplicationListener 这个泛型类的参数类型(事件类),注意这里的实现方法只能对通过 JDK 实例化策略生成的监听者对象,而对 CGLIB 代理产生的代理对象则无效。因此,这里需要将 BeanFactory 的实例化策略修改为 SimpleInstantiationStrategy. 判断监听者感兴趣的事件类是否是目标事件类或者其父类时,主要用到了 isAssignableFrom 方法, A.isAssignableFrom(B) 表示 A 是 B 的父类

    package io.github.shengdoupi.springframework.context.event;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description
     */
    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
        
        @Override
        public void multicastEvent(ApplicationEvent applicationEvent) {
            for (ApplicationListener applicationListener : getApplicationListeners(applicationEvent)) {
                applicationListener.onApplicationEvent(applicationEvent);
            }
        }
    }

获取感兴趣的监听器集合,执行回调;

  1. 定义抽象事件发布者

    package io.github.shengdoupi.springframework.context;

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description Interface that encapsulates event publish functionality.
     */
    public interface ApplicationEventPublisher {
        
        /**
         * Notify all matching listeners registered with this application of an event.
         * @param event
         */
        void publishEvent(ApplicationEvent event);
    }

万物皆抽象,事件发布者和事件广播器做一个解耦,方便扩展;

  1. 容器初始化刷新事件框架

    package io.github.shengdoupi.springframework.context.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     */
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
        
        private static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
        
        private ApplicationEventMulticaster applicationEventMulticaster;
        
        @Override
        public void refresh() throws BeansException {
            try {
                ...
                // 4. 初始化事件广播器
                initApplicationEventMulticaster();
                // 5. 注册事件监听器
                registerListeners();
                // 4. 完成实例化单例Bean
                finishBeanFactoryInitialization(beanFactory);
            } catch (Exception e) {
                throw new BeansException("Application context refresh error", e);
            }
        }
        
        ...
      
        public void initApplicationEventMulticaster() {
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
        }
        
        public void registerListeners() {
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            Map<String, ApplicationListener> applicationListeners = beanFactory.getBeansOfType(ApplicationListener.class);
            for (ApplicationListener applicationListener : applicationListeners.values()) {
                applicationEventMulticaster.addApplicationListener(applicationListener);
            }
        }
        
        @Override
        public void publishEvent(ApplicationEvent event) {
            applicationEventMulticaster.multicastEvent(event);
        }
    }

容器刷新过程中,初始化事件广播器,同时注册所有的事件监听器 Bean 到注册表中;

此外,容器对外暴露事件发布接口,对使用者来说容器作为事件发布者执行事件发布;

  1. 容器生命周期事件

    package io.github.shengdoupi.springframework.context.event;

    import io.github.shengdoupi.springframework.context.ApplicationContext;
    import io.github.shengdoupi.springframework.context.ApplicationEvent;

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description Base class for events raised for an ApplicationContext.
     */
    public abstract class ApplicationContextEvent extends ApplicationEvent {
        public ApplicationContextEvent(ApplicationContext source) {
            super(source);
        }
        
        /**
         * Get ApplicationContext that the event was raised for.
         * @return
         */
        public ApplicationContext getApplicationContext() {
            return (ApplicationContext) getSource();
        }
    }

    package io.github.shengdoupi.springframework.context.event;

    import io.github.shengdoupi.springframework.context.ApplicationContext;

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description Event raised when an ApplicationEvent get initialized or refreshed.
     */
    public class ContextRefreshedEvent extends ApplicationContextEvent{
        
        /**
         * Create a new ContextRefreshedEvent.
         * @param source
         */
        public ContextRefreshedEvent(ApplicationContext source) {
            super(source);
        }
    }

    package io.github.shengdoupi.springframework.context.event;

    import io.github.shengdoupi.springframework.context.ApplicationContext;

    /**
     * @author zhoukehh
     * @date 2024/5/8
     * @description Event raised when an ApplicationContext is closed.
     */
    public class ContextClosedEvent extends ApplicationContextEvent{
        
        /**
         * Create a ContextClosedEvent.
         * @param source
         */
        public ContextClosedEvent(ApplicationContext source) {
            super(source);
        }
    }

Spring 提供了与容器生命周期相关的事件,包括容器刷新、关闭等,在容器到达特定生命周期时触发;

    package io.github.shengdoupi.springframework.context.support;

    import ...

    /**
     * @author zhoukehh
     * @date 2024/4/27
     * @description
     */
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
       
        ... 
        @Override
        public void refresh() throws BeansException {
            try {
                ...
              
                // 7. 完成容器刷新
                finishRefresh();
            } catch (Exception e) {
                throw new BeansException("Application context refresh error", e);
            }
        }
        
        ...
      
        @Override
        public void close() {
            // 发布容器关闭事件
            publishEvent(new ContextClosedEvent(this));
            
            // 执行单例 Bean 的销毁方法
            getBeanFactory().destroySingletons();
        }
        
        private void finishRefresh() {
            publishEvent(new ContextRefreshedEvent(this));
        }
      
       ...

    }

应用上下文在特定生命周期发布相关事件;

Spring 通过观察者模式实现了事件机制,主要包括事件源定义、事件监听机制和事件发布;

  • 事件源:事件的主体,同时包含"事件发生地"这个属性;
  • 事件监听:监听者的抽象定义、注册,核心逻辑是在容器的刷新过程中将监听者注册到注册表中;
  • 事件发布:事件发布者的抽象定义、事件广播器的具体实现,核心逻辑是事件发生时进行广播,判断对该事件感兴趣的监听者集合,进行回调;

总结

本节我们才算真正的完成了创建对象的 IoC(控制反转)框架。具体体现在我们可以通过简单配置的方式就能获取一个 Bean ,和手写代码完成了解耦,将创建对象的能力给了框架。此外,框架通过在自动化创建 Bean 的过程中埋下扩展点,以及  Aware 机制和 FactoryBean 的设计,让我们可以对 Bean 在配置的基础上做一些额外的扩展。最后,还通过事件机制让我们 Bean 工厂的生命周期对业务可见。

spring-02 (1).png

03 总结一下

这篇文章我们实现了一个较为完整的 Spring Bean 工厂模块,从最简单的对象缓存(01.1),到支持含参构造函数、属性注入,我们可以缓存的对象逐渐更接近我们平时使用的对象。

再后来,我们发现这个 Bean 工厂使用起来还是比较麻烦,好像只有一个缓存的作用,获取一个对象还是要写很多代码。为此,我们引入了配置化模块:Bean 定义文件自动加载,引入了自动化模块:应用上下文自动创建 BeanFactory,给工厂和 Bean 创建提供一个完全 IoC 的生命周期(不需要业务人员干预)。

最后,我们不想让一个 Bean 对象的创建那么拘泥于我们的初始配置,因为有些逻辑不是可以通过配置解决的,比如创建时进行资源加载等。为此,我们又在容器和 Bean 的生命周期中埋下了各种扩展点,包括 BeanFactoryPostProcessor、BeanPostProcessor 以及初始化和销毁方法等。

可见这个 Bean 工厂在我们手下一步步变得完整起来,下一节我们将对 Spring 的另一个核心基础功能 AOP 做学习实现。

引用

  1. bugstack.cn/md/spring/d…
  2. spring-framework