5. Spring源码篇之BeanDefinition

简介

在spring中BeanDefinition是一个接口,下面也有很多的实现类

大致如下

text 复制代码
BeanDefinition
    AbstractBeanDefinition
        RootBeanDefinition              最终每个BeanDefinition都是一个 RootBeanDefinition
        ChildBeanDefinition             现在不用了废弃,现在使用的都是 GenericBeanDefinition
        GenericBeanDefinition           我们定义的一般都是 GenericBeanDefinition
        ScannedGenericBeanDefinition    是GenericBeanDefinition的一个子类,表示是通过scanner扫描出来的Bean
        AnnotatedGenericBeanDefinition  是GenericBeanDefinition的一个子类,表示是通过reader注册的Bean

属性

text 复制代码
beanClass           bean对应的类,是一个object类型,在还没有实例化单例Bean的时候放入的是类全限定名,实例化后就是class
scope               单例 多例等
abstractFlag        标志无法实例化
lazyInit            懒加载
autowireMode        AUTOWIRE_NO,AUTOWIRE_BY_NAME,AUTOWIRE_BY_TYPE,AUTOWIRE_CONSTRUCTOR,AUTOWIRE_AUTODETECT
dependsOn           依赖于其它bean,其它bean会先初始化
instanceSupplier    对象来源于Supplier
factoryBeanName     factoryBean的名字
factoryMethodName   @Bean的时候会有值
initMethodName      初始化方法
destroyMethodName   销毁方法

mergedBeanDefinitions

在spring中并没有该类的定义,但却十分重要

private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

一般来说我们的bean都是GenericBeanDefinition,其实也是RootBeanDefinition,但是在xml方式下,可以设置一个BeanDefinition为abstract的,可供其它bean继承它,从而得到自己没有定义的属性

例如

spring.xml

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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	   https://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
    <bean id="abs" scope="prototype" abstract="true"/>
    <bean id="userBean" class="com.shura.beans.UserBean" parent="abs"/>
</beans>

使用xml方式

java 复制代码
public class Application {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
		System.out.println(context.getBean(UserBean.class));
		System.out.println(context.getBean(UserBean.class));
		System.out.println(context.getBean(UserBean.class));

	}
}

输出
com.shura.beans.UserBean@7506e922
com.shura.beans.UserBean@4ee285c6
com.shura.beans.UserBean@621be5d1

从上面例子可以看出,定义了一个abs的抽象 BeanDefinition userBean继承了它,从而获得了多例属性

那么在注册bean的时候就势必要找出父BeanDefinition,继承属性,于是就有了mergedBeanDefinitions

在实例化单例Bean的时候就有这一步骤

方法

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

下面是简化的源码

java 复制代码
protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {

    synchronized (this.mergedBeanDefinitions) {
        RootBeanDefinition mbd = null;
        RootBeanDefinition previous = null;
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }

        // 如果合并过就跳过
        if (mbd == null || mbd.stale) {
            previous = mbd;
            // 是否有parent,大部分都是进入该分支,如果进入该分支,那么本身的BeanDefinition就是RootBeanDefinition
            if (bd.getParentName() == null) {
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                } else {
                    mbd = new RootBeanDefinition(bd);
                }
            } else {
                // 进入这里表示有父BeanDefinition parent != null
                BeanDefinition pbd;
                try {
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
                        // 寻找父BeanDefinition的父BeanDefinition,知道找不到
                        pbd = getMergedBeanDefinition(parentBeanName);
                    } else {
                        // 抛出异常
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                        } else {
                            throw new NoSuchBeanDefinitionException(parentBeanName,
                                    "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                            "': cannot be resolved without a ConfigurableBeanFactory parent");
                        }
                    }
                } catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                            "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                }

                // 覆盖属性,得到一个新的RootBeanDefinition
                mbd = new RootBeanDefinition(pbd);
                mbd.overrideFrom(bd);
            }

            if (!StringUtils.hasLength(mbd.getScope())) {
                // 默认单例
                mbd.setScope(SCOPE_SINGLETON);
            }
            
            if (containingBd == null && isCacheBeanMetadata()) {
                // 缓存起来
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }
        if (previous != null) {
            // copy属性
            copyRelevantMergedBeanDefinitionCaches(previous, mbd);
        }
        return mbd;
    }
}

上面就是merge的逻辑,大致就是,如果有父BeanDefinition就合并属性得到一个新的RootBeanDefinition,返回

本文主要介绍了BeanDefinition以及合并的过程,下一节介绍FactoryBean


欢迎关注,学习不迷路!

相关推荐
10km24 分钟前
java:Apache Commons Configuration2占位符解析异常的正确解法:${prefix:name:-default}
java·apache·configuration2·变量插值·interpolation
customer0825 分钟前
【开源免费】基于SpringBoot+Vue.JS个人博客系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
灰色人生qwer32 分钟前
SpringBoot 项目配置日志输出
java·spring boot·后端
2301_7930698242 分钟前
Spring Boot +SQL项目优化策略,GraphQL和SQL 区别,Spring JDBC 等原理辨析(万字长文+代码)
java·数据库·spring boot·sql·jdbc·orm
阿华的代码王国1 小时前
【从0做项目】Java搜索引擎(6)& 正则表达式鲨疯了&优化正文解析
java·后端·搜索引擎·正则表达式·java项目·从0到1做项目
服务端相声演员1 小时前
Oracle JDK、Open JDK zulu下载地址
java·开发语言
是姜姜啊!1 小时前
java连接redis
java·redis
hhw1991121 小时前
spring boot知识点5
java·数据库·spring boot
EQUINOX11 小时前
lab4 CSAPP:Cachelab
java·后端·spring
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS打卡健康评测系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源