手撕Spring总结,Bean生命周期从始至终

175 阅读9分钟

前言

最近通过阅读 小傅哥 的手撕Spring系列文章,跟着源码敲了一遍。 此文通过Bean生命周期对简化版Spring的执行流程进行总结。

源码仓库https://gitee.com/ithuameng/small-spring

Bean 生命周期

7dc4a10d01d04d21aeb89996a623ebda.png

Xml 配置文件与 Refresh 方法

  1. 准备 xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
	 http://www.springframework.org/schema/context">>
    <!--目标对象-->
    <bean id="userService" class="com.ithuameng.springframework.test.bean.UserService3"/>
    <!--注册 BeanPostProcessor-->
    <bean class="com.ithuameng.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    <!--自定义环绕拦截-->
    <bean id="beforeAdvice" class="com.ithuameng.springframework.test.advice.UserServiceBeforeAdvice"/>
    <!--方法拦截器-->
    <bean id="methodInterceptor"
          class="com.ithuameng.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
        <property name="advice" ref="beforeAdvice"/>
    </bean>
    <!--pointcutAdvisor 切面配置-->
    <bean id="pointcutAdvisor" class="com.ithuameng.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(* com.ithuameng.springframework.test.bean.IUserService.*(..))"/>
        <property name="advice" ref="methodInterceptor"/>
    </bean>
    <context:component-scan base-package="com.ithuameng.springframework.test.bean"/>
</beans>

可以 自定义Bean配置AOP代理,也可以 使用注解 @Component,通过 component-scan 来扫描。

  1. 测试方法使用 ClassPathXmlApplicationContext 加载 xml配置文件:
@Test
public void test_xml() {
    // 1.初始化 BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(CLASSPATH_URL_PREFIX + "spring.xml");
    // 注册虚拟机钩子
    applicationContext.registerShutdownHook();
}
  1. 通过 ClassPathXmlApplicationContext 构造方法执行 Spring 核心方法 refresh
@Override
public void refresh() throws BeansException {
    // 1.创建 BeanFactory,并加载 BeanDefinition
    refreshBeanFactory();
    
    // 2.获取 BeanFactory()
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
    // 3.添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 4.在 Bean 实例化之前,执行 BeanFactoryPostProcessor
    invokeBeanFactoryPostProcessors(beanFactory);
    
    // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
    registerBeanPostProcessors(beanFactory);
    
    // 6. 初始化事件发布者
    initApplicationEventMulticaster();
    
    // 7. 注册事件监听器
    registerListeners();
    
    // 8.设置类型转换器、提前实例化单例 Bean对象
    finishBeanFactoryInitialization(beanFactory);
    
    // 9. 发布容器刷新完成事件
    finishRefresh();
}

注:下文将通过这个 refresh 方法来解析各方面的执行流程。

BeanDefinition

一个 BeanDefinition 描述了一个 Bean 实例,实例包含属性值、构造方法参数值以及更多实现信息。主要目的是允许修改属性值和其他 Bean 元数据。

BeanDefinition 一些属性信息:

public class BeanDefinition {
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    private Class beanClass;
    private PropertyValues propertyValues;      // Bean属性值
    private String initMethodName;              // Bean初始化方法
    private String destroyMethodName;           // Bean销毁方法
    private String scope = SCOPE_SINGLETON;     // Bean作用域
    private boolean singleton = true;
    private boolean prototype = false;
}

通过 refresh 方法第一行代码 refreshBeanFactory() 来加载 xml 中定义的 Bean 信息。

@Component 注解扫描

xml 使用了如下的组件扫描配置:

<context:component-scan base-package="com.ithuameng.springframework.test.bean"/>

Java 代码判断是否使用了 component-scan:

// 解析 context:component-scan 标签,扫描包中的类并提取相关信息,用于组装 BeanDefinition
Element componentScan = root.element("component-scan");
if (null != componentScan) {
    String scanPath = componentScan.attributeValue("base-package");
    if (StrUtil.isEmpty(scanPath)) {
        throw new BeansException("The value of base-package attribute can not be empty or null");
    }
    scanPackage(scanPath);
}

通过Java代码对 xml 中配置的包路径进行扫描,判断是否有使用了 @Component 注解的类,若有则将其定义为 BeanDefinition 注册进 beanDefinitionMap 中:

public void doScan(String... basePackages) {
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition beanDefinition : candidates) {
            // 解析 Bean的作用域 singleton、prototype
            String beanScope = resolveBeanScope(beanDefinition);
            if (StrUtil.isNotEmpty(beanScope)) {
                beanDefinition.setScope(beanScope);
            }
            // 注册 Bean信息
            registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
        }
    }
    // 注册处理注解的 BeanPostProcessor (@Autowired、@Value)
    registry.registerBeanDefinition("internalAutowiredAnnotationProcessor", 
            new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));
}

findCandidateComponents 方法如下:

/**
 * 这里先要提供一个可以通过配置路径 basePackage=com.ithuameng.springframework.test.bean
 * 通过这个方法就可以扫描到所有 @Component 注解的 Bean 对象了
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);
    for (Class<?> clazz : classes) {
        candidates.add(new BeanDefinition(clazz));
    }
    return candidates;
}

解析 xml 自定义

xml 配置文件中,可以自定义各种各样的 Bean,使用 Java进行解析处理,将其注册到 beanDefinitionMap 中:

List<Element> beanList = root.elements("bean");
for (Element bean : beanList) {
    String id = bean.attributeValue("id");
    String name = bean.attributeValue("name");
    String className = bean.attributeValue("class");
    String initMethod = bean.attributeValue("init-method");
    String destroyMethodName = bean.attributeValue("destroy-method");
    String beanScope = bean.attributeValue("scope");
    // 获取 Class,方便获取类中的名称
    Class<?> clazz = Class.forName(className);
    // 优先级 id > name
    String beanName = StrUtil.isNotEmpty(id) ? id : name;
    if (StrUtil.isEmpty(beanName)) {
        beanName = StrUtil.lowerFirst(clazz.getSimpleName());
    }
    // 定义 Bean
    BeanDefinition beanDefinition = new BeanDefinition(clazz);
    beanDefinition.setInitMethodName(initMethod);
    beanDefinition.setDestroyMethodName(destroyMethodName);
    if (StrUtil.isNotEmpty(beanScope)) {
        beanDefinition.setScope(beanScope);
    }
    // 读取属性并填充
    List<Element> propertyList = bean.elements("property");
    for (Element property : propertyList) {
        // 解析标签:property
        String attrName = property.attributeValue("name");
        String attrValue = property.attributeValue("value");
        String attrRef = property.attributeValue("ref");
        // 获取属性值:引入对象、值对象
        Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
        // 创建属性信息
        PropertyValue propertyValue = new PropertyValue(attrName, value);
        beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
    }
    // 判断 bean是否已经定义
    if (getRegistry().containsBeanDefinition(beanName)) {
        throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
    }
    // 注册 BeanDefinition
    getRegistry().registerBeanDefinition(beanName, beanDefinition);
}

BeanFactoryPostProcessor

Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean之前读取bean的定义(配置元数据),通过beanFactory可以获取bean的定义信息,并 可以修改bean的定义信息

refresh方法中第四行代码执行 BeanFactoryPostProcessor 对 BeanDefinition 进行修改:

// 4.在 Bean 实例化之前,执行 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);

方法中使用 BeanFactory 获取所有实现 BeanFactoryPostProcessor 接口的 Bean,执行其中的 postProcessBeanFactory 方法:

private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
    for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
        beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
    }
}

可以通知自定义 BeanFactoryPostProcessor 接口的实现类,进行对某个 BeanDefinition 进行修改,例如下面的实现类,对 userService 这个 Bean定义信息进行修改:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));
    }
}

将其定义在 xml 配置文件中,交由 Spring IOC 管理:

<bean class="com.ithuameng.springframework.test.common.MyBeanFactoryPostProcessor"/>

BeanPostProcessor

BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。BeanPostProcessor的源码如下:

/**
 * Spring 提供的扩展机制
 * 在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象
 * 这部分与后面要实现的 AOP 有着密切的关系
 *
 * @author ithuameng
 * @date 2022-7-19
 */
public interface BeanPostProcessor {
    /**
     * 在 Bean 对象执行初始化方法之前,执行此方法
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    /**
     * 在 Bean 对象执行初始化方法之后,执行此方法
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

其中 postProcessBeforeInitialization 方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization 方法会在每个bean对象的初始化方法调用之后被回调。

BeanPostProcessor 与 AOP 的实现有着密切的关系。

Bean实例化

Bean 实例化是整个 Bean生命周期中最为重要的地方,也是整个 IOC 与 AOP 体现所在。

创建 Bean 实例

protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
    Constructor constructorToUse = null;
    Class<?> beanClass = beanDefinition.getBeanClass();
    Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
    for (Constructor ctor : declaredConstructors) {
        if (null != args && ctor.getParameterTypes().length == args.length) {
            constructorToUse = ctor;
            break;
        }
    }
    return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}

提供了两种实例化 Bean 的方法,使用了策略模式进行算法选择:

// 默认使用 JDK 实例化 Bean对象
private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

Bean 三级缓存

循环依赖问题

ad4e1bf18339435a9c07ad7b01df712a.png

  • 循环依赖主要分为这三种,自身依赖于自身、互相循环依赖、多组循环依赖。
  • 无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。

三级缓存就为了解决 Bean之间相互注入收发的循环依赖问题,一级缓存存放的是完整的 Bean 实例,二级缓存存放的是代理之后的 Bean 实例,三级缓存存放的是代理对象方法

实例一个 Bean 首先需要向缓存中获取,如果缓存中已经有这个 Bean 则直接返回,没有则需要创建 Bean:

@Override
public Object getSingleton(String beanName) {
    // 一级缓存
    Object singletonObject = singletonObjects.get(beanName);
    if (null == singletonObject) {
        // 二级缓存
        singletonObject = earlySingletonObjects.get(beanName);
        if (null == singletonObject) {
            // 三级缓存
            ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                // 把三级缓存中的代理对象中的真实对象获取出来,放入二级缓存中
                earlySingletonObjects.put(beanName, singletonObject);
                singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

上面使用策略算法实例化 Bean之后,将没有完全实例化的 Bean存放到第三级缓存中:

// 创建 Bean实例
bean = createBeanInstance(beanDefinition, beanName, args);
// 处理循环依赖,将实例化后的 Bean 对象提前放入缓存中暴露出来
if (beanDefinition.isSingleton()) {
    Object finalBean = bean;
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
}

从三级缓存中获取数据的时候,会使用 AOP 对实例的 Bean 对象进行代理,如果 AOP 没有进行配置,则会返回之前实例的 Bean 保存到 第二级缓存中,将第三级缓存中的数据删除。

Bean 填充属性

之前在介绍解析 xml 配置文件时,已经将 Bean的属性信息解析配置到 PropertyValue 中,不过还有一种是使用了注解的方式进行属性内容注入,例如 @Value@Autowired 这两个注解,这个时候我们就需要手动的对使用了注解的属性进行解析:

@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
    // 1.处理注解 @Value
    Class<?> clazz = bean.getClass();
    clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field field : declaredFields) {
        Value valueAnnotation = field.getAnnotation(Value.class);
        if (null != valueAnnotation) {
            String value = valueAnnotation.value();
            value = beanFactory.resolveEmbeddedValue(value);
            BeanUtil.setFieldValue(bean, field.getName(), value);
        }
    }
    // 2. 处理注解 @Autowired
    for (Field field : declaredFields) {
        Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
        if (null != autowiredAnnotation) {
            Class<?> fieldType = field.getType();
            String dependentBeanName = null;
            Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
            Object dependentBean = null;
            if (null != qualifierAnnotation) {
                dependentBeanName = qualifierAnnotation.value();
                dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
            } else {
                dependentBean = beanFactory.getBean(fieldType);
            }
            BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
        }
    }
    return pvs;
}

解析完之后存放到 PropertyValues 中,就可以给 Bean 进行属性填充了:

// 属性填充
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
    try {
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {
            String name = propertyValue.getName();
            Object value = propertyValue.getValue();
            // Bean引用对象  获取容器中的 Bean
            if (value instanceof BeanReference) {
                BeanReference beanReference = (BeanReference) value;
                value = getBean(beanReference.getBeanName());
            }
            // 类型转换
            else {
                Class<?> sourceType = value.getClass();
                Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);
                ConversionService conversionService = getConversionService();
                if (conversionService != null) {
                    if (conversionService.canConvert(sourceType, targetType)) {
                        value = conversionService.convert(value, targetType);
                    }
                }
            }
            // 反射设置属性填充
            BeanUtil.setFieldValue(bean, name, value);
        }
    } catch (Exception e) {
        throw new BeansException("Error setting property values:" + beanName);
    }
}

Aware 感知

Aware 感知指的是 Bean 实现某些接口,可以获取应用程序上下文中的一些对象信息,比如 BeanFactory、ClassLoader 等。

例如定义一个 BeanFactoryAware 的接口,标识需要获取 BeanFactory 对象信息:

public interface BeanFactoryAware extends Aware {
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

spring 中的 AutowiredAnnotationBeanPostProcessor 类实现这个接口:

/**
 * 扫描自定义注解
 *
 * @author ithuameng
 * @date 2022-8-1
 */
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }
}

执行 Bean的初始化方法时获取这些感知对象:

// spring容器 Aware感知
if (bean instanceof Aware) {
    if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(this);
    }
    if (bean instanceof BeanClassLoaderAware) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
    }
    if (bean instanceof BeanNameAware) {
        ((BeanNameAware) bean).setBeanName(beanName);
    }
}

这样就可以使用这些感知对象了。

Bean 对象的初始化方法

Bean 初始化方法来源于实现了 InitializingBean 接口或者是在 xml 中配置了 init-method 信息,调用 invokeInitMethods 执行 Bean的初始化方法:

private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
    // 1.Bean 实现了 InitializingBean接口
    if (bean instanceof InitializingBean) {
        ((InitializingBean) bean).afterPropertiesSet();
    }
    // 2.配置信息 init-method
    String initMethodName = beanDefinition.getInitMethodName();
    if (StrUtil.isNotEmpty(initMethodName)) {
        Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
        if (null == initMethod) {
            throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
        }
        initMethod.invoke(bean);
    }
}

在执行这个初始化方法之前,将会执行 BeanPostProcessor Before 处理:

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (null == current) {
            return result;
        }
        result = current;
    }
    return result;
}

在执行这个初始化方法之后,将会执行 BeanPostProcessor After 处理:

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (null == current) {
            return result;
        }
        result = current;
    }
    return result;
}

Bean 对象的销毁方法

Bean 销毁方法来源于实现了 DisposableBean 接口或者是在 xml 中配置了 destroy-method 信息。我们需要在应用上下文中注册虚拟机钩子:

Runtime.getRuntime().addShutdownHook(new Thread(this::close));

当程序关闭时,执行程序上下文中的 close 事件,调用 Bean 的 destory方法:

@Override
public void destroy() throws Exception {
    // 1.bean实现接口 DisposableBean
    if (bean instanceof DisposableBean) {
        ((DisposableBean) bean).destroy();
    }
    // 2.配置信息 destroy-method {判断是为了避免二次执行销毁}
    if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
        Method initMethod = bean.getClass().getMethod(destroyMethodName);
        if (null == initMethod) {
            throw new BeansException("Could not find an destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
        }
        initMethod.invoke(bean);
    }
}

总结

这个简化版 spring 还实现了容器事件和事件监听器,类型转换器等功能,具体的实现不一一演示。

此文用于记录 spring 中 Bean 的生命周期是如何进行的,用于巩固已学知识。

学海无涯,作者:浪子花梦,写于:2022 / 9 / 2