超详细分析Spring的BeanDefinition

1,285 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情

前言

BeanDefinitionSpring中的十分重要的一个类,所有bean的前生都是BeanDefinition,学习SpringbootSpring时,会经常遇到该类,所以本篇文章会对BeanDefinition的概念进行入门学习,同时一并学习定义注册,移除和查询BeanDefinitionBeanDefinitionRegistry接口。

Springboot版本:2.4.1

正文

一. BeanDefinition简析

BeanDefinitionSpring中的重要接口,BeanDefinition的实现类用于描述Spring中的一个应该被实例化的bean的各种性质,包括bean的属性值,构造函数,方法等信息,除此之外,还额外描述beanSpring容器中的作用域,bean名称等信息。

可以将BeanDefinition类比于Java中的类的Class对象,在Java中可以使用一个类的Class对象来完成对象的实例化,但是在Spring中,单纯使用beanClass对象无法完成bean的实例化,因为Spring中的bean具备一些额外的性质,例如bean是否是单例,bean是否在容器中是懒加载,bean在容器中的名字,这些性质无法依靠类的Class对象来描述,所以Spring引入BeanDefinition来描述Spring中的一个应该被实例化的bean的各种性质。

Spring框架在启动时,会在ConfigurationClassPostProcessor这个bean工厂后置处理器中将需要被加载到容器中的bean扫描到并创建BeanDefinition,然后缓存到BeanFactorybeanDefinitionMap中,beanDefinitionMap是一个Map,用于存放BeanDefinition,键为bean在容器中的名称,值为bean对应的BeanDefinition

示例工程中在配置类中定义了一个bean,如下所示。

@Service
public class MyService {

    public MyService() {
        System.out.println("MyService init.");
    }

}

@Configuration
public class MyConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }

}

已知在初始化容器时,初始化逻辑在AbstractApplicationContextrefresh() 方法中,如下所示。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        prepareRefresh();

        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        prepareBeanFactory(beanFactory);

        try {
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");

            // 在这里调用到ConfigurationClassPostProcessor的逻辑
            // 将需要注册到容器中的bean加载为BeanDefinition并存放到BeanFactory的beanDefinitionMap中
            invokeBeanFactoryPostProcessors(beanFactory);

            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            initMessageSource();

            initApplicationEventMulticaster();

            onRefresh();

            registerListeners();

            // 初始化bean
            finishBeanFactoryInitialization(beanFactory);

            finishRefresh();
        }

        catch (BeansException ex) {
            // 省略
        }

        finally {
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

现在将断点打到AbstractApplicationContextrefresh() 方法中调用invokeBeanFactoryPostProcessors() 方法的这一行代码,此时观察BeanFactory中的beanDefinitionMap如下所示。

BeanDefinition加载前的存储示意图

此时往后执行一步,再观察BeanFactory中的beanDefinitionMap如下所示。

BeanDefinition加载后的存储示意图

可以看到BeanFactory中的beanDefinitionMap多了事先定义好的MyServiceBeanDefinition,这是因为在ConfigurationClassPostProcessor中会将需要被加载到容器中的bean都扫描出来并创建成BeanDefinition,然后存放到beanDefinitionMap中,但是MyService的构造函数中应该被打印的信息是没有被打印的,这说明ConfigurationClassPostProcessor中只会创建BeanDefinition并存放到beanDefinitionMap中,不会实际的实例化bean,真正的bean的实例化由AbstractApplicationContextfinishBeanFactoryInitialization() 方法开启,这里暂不分析。

现在可知,Spring借助BeanDefinition来创建容器中的bean,容器中的每一个bean都会由一个BeanDefinition来描述,描述包括bean属性值,构造函数,方法,bean作用域,bean名称等信息,Spring在启动时会先扫描所有需要被加载到容器中的bean,然后为这些bean创建BeanDefinition并添加到BeanFactory中的beanDefinitionMap中。创建BeanDefinition时不会实例化beanbean的实例化在BeanDefinition创建之后。

二. BeanDefinitionRegistry简析

Spring中会根据配置的不同使用不同的容器,但无论使用哪种容器,其内部都会持有一个BeanFactory容器,其实际类型为DefaultListableBeanFactoryDefaultListableBeanFactory是一个具有注册功能的容器,因为其实现了BeanDefinitionRegistry接口。

BeanDefinitionRegistry接口定义了对BeanDefinition的注册,移除和查询等操作,下面主要看一下DefaultListableBeanFactoryBeanDefinition的注册的实现,即registerBeanDefinition() 方法,源码如下。

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    // 通过beanName将已经注册的BeanDefinition获取出来
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        // 如果已经使用当前beanName注册过BeanDefinition
        // 则判断是否允许以相同beanName注册不同的BeanDefinition以覆盖已存在的BeanDefinition
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            if (logger.isInfoEnabled()) {
                logger.info("Overriding user-defined bean definition for bean '" + beanName +
                        "' with a framework-generated bean definition: replacing [" +
                        existingDefinition + "] with [" + beanDefinition + "]");
            }
        }
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                        "' with a different definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                        "' with an equivalent definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        // 检查bean实例是否已经开始创建
        if (hasBeanCreationStarted()) {
            synchronized (this.beanDefinitionMap) {
                // 将BeanDefinition缓存到beanDefinitionMap
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 将beanName缓存到beanDefinitionNames
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                // 从manualSingletonNames中将beanName移除,以防止beanName重复
                // manualSingletonNames中缓存了手动注册的单例的名称
                removeManualSingletonName(beanName);
            }
        }
        else {
            // 仍处于启动注册阶段
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
    else if (isConfigurationFrozen()) {
        clearByTypeCache();
    }
}

DefaultListableBeanFactory实现的registerBeanDefinition() 方法中,会将beanNameBeanDefinition以键值对的形式缓存到beanDefinitionMap中,同时还会将beanName添加到beanDefinitionNames中。由于还会存在手动注册单例bean的情况,如果手动注册了单例bean,单例bean的名称会缓存在manualSingletonNames中,所以还需要在registerBeanDefinition() 方法中保证beanDefinitionNamesmanualSingletonNames中的beanName不重复。

现在做一个小节,在Spring启动时,创建的容器中会持有一个DefaultListableBeanFactory容器,其实现了BeanDefinitionRegistry接口,具备对BeanDefinition进行注册,删除和查询等功能,Spring进行bean的扫描加载时均会基于需要加载到容器中的bean创建BeanDefinition,并将创建好的BeanDefinition注册到DefaultListableBeanFactory容器中。

总结

BeanDefinition的主要作用是描述Spring中的bean,作用可以类比于Java中的Class对象,但是比Class对象能够描述更多的bean信息,例如bean作用域,是否懒加载等。在Spring的启动阶段,每个需要被加载到容器中的bean会被创建为一个BeanDefinition,然后被注册到容器中,而Spring中使用的容器均持有一个DefaultListableBeanFactory,其实现了BeanDefinitionRegistry接口,所以注册BeanDefinition到容器中,实际就是注册BeanDefinitionDefaultListableBeanFactory中,最终每个BeanDefinition会被缓存到DefaultListableBeanFactorybeanDefinitionMap中。


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情