概述
开发人员写的类被spring扫描到容器后,并不是一开始就是一个完整的Bean了,spring扫描的是被注解或者是xml配置的类的信息,spring将类的信息或者是xml的信息保存在BeanDefinition的数据结构中,然而,如果我们想要得到一个可以使用的对象,是需要由spring来创建,这也就是网上常说的将bean的管理和创建交给spring
由于bean的生命十分复杂,涉及的点也比较多,本文主要对BeanDefintion的合并进行解析,而关于生命周期的后面的内容会陆续跟新
生命周期
首先简单看一下生命周期前的方法调用栈
从方法名字就可以看出核心的方法preInstantiateSingletons,在这里面就会完成spring 的单例Bean的初始化
接下来直接进入preInstantiateSingletons
在preInstantiateSingletons这个方法里面有两个for循环,我们来看第一个
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
首先我们注意到的是,现在spring中去获取了一个List集合,那么这个集合是什么呢???
前面说到过spring在扫描对应包下的类后,会读取类的信息然后存储到BeanDefinition这个数据结构中
这里获取到的正是这个数据结构的集合,接下来的操作就应该很容易到了,即然已经获取到了类的元数据集合
那么我们直接循环遍历,根据元数据创建对象就好了,而spring 也真是这样做的
但是循环的第一步却没有直接做更创建对象直接相关的事情,这里调用了getMergedLocalBeanDefinition(beanName)
这个方法的作用是对于BeanDefinition的合并,下面先进入这个方法
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
Merged的意思是合并,这里是在合并BeanDefinition,那么什么情况要进行BeanDefinition的合并呢?
<bean name="myBean" abstract="true" class="com.service.myBean"/>
<bean name="myBeanImpl" class="com.service.myBeanImpl" parent="myBean">
</bean>
有关于涉及Bean的合并一般而言是正对xml的配置,通过上面的xml配置可以观察到一个parent的属性
这个属性指向myBean,就可以理解为myBean是myBeanImpl的parent,这里跟java的继承有点像
类似于子类会继承父类的一些特性,同时子类也能覆盖父类的特性,这里也是一样的
例如:如果我们在myBean的标签里面添加上 scope=“prototype” ,那么对于myBeanImpl而言,它的scope=“prototype”
至于这个属性到底是怎么继承过来的呢?就是通过这里的getMergedLocalBeanDefinition方法
此外还有一点也需要注意我们可以看到在preInstantiateSingletons这个方法中返回的是RootBeanDefinition
这是BeanDefintion的一个子类,它和别的BeanDefintion的有什么区别么?
答案是有的,我们通过xml扫描进入到spring容器中,会将xml中的信息解析然后封装GenericBeanDefinition中
这个类也是一个子类但是于rootBeanDefinition相比,他们的不同如下
rootBeanDefiniton
@Override
public String getParentName() {return null;}
@Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
GenericBeanDefinition
@Override
public void setParentName(@Nullable String parentName) {this.parentName = parentName;}
@Override
@Nullable
public String getParentName() {return this.parentName;}
不论是GenericBeanDefinition还是rootBeanDefintion都是BeanDefinition的子类,他们的功能大同小异
而其中主要的区别也很容易看出来
对于rootBeanDefiniton的Parent属性的get/set方法,一个是返回null,另外一个是抛出异常
而对于GenericBeanDefinition的parent的get/set方法则是普通的get/set
这样做的目的其实是不允许RootBeanDefintion拥有父类,表示最顶级的,不需要合并的类,或者表示为已经被合并完毕的类
而getMergedLocalBeanDefinition这个方法的目的也非常明显了,就是为了找到合并完之后的BeanDefinition了
在这里我认为面向接口编程的好处就能体现出来了!!!
1.灵活性高,不论是rootBeanDefinition还是genericBeanDefinition都是BeanDefinition的子类
所以在使用上可以根据需要灵活切换
2.代码复用性高,只是根据不同的特点,修改部分方法实现就可以了
3.同时在我认为rootBeanDefinition和genericBeanDefinition他们之间不是没有关系的,他们有共同的父类,
但是彼此之间又不会有影响,这样的代码其实跟容易给人模块化的感觉,充分发挥面向对象编程的优势
如何进行合并
接下我们来研究一下BeanDefinition具体是怎么进行合并的呢?
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;
if (bd.getParentName() == null) {
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
else {
BeanDefinition pbd;
try {
String parentBeanName = transformedBeanName(bd.getParentName());
if (!beanName.equals(parentBeanName)) {
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {//异常代码省略}
}
}
catch (NoSuchBeanDefinitionException ex) {//异常代码省略}
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);
}
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(SCOPE_SINGLETON);
}
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
if (containingBd == null && isCacheBeanMetadata()) {this.mergedBeanDefinitions.put(beanName, mbd);}
}
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}
最终进行合并的是上面的这个方法,先介绍参数
参数1:这里传入过来的是我们当前正要创建Bean的BeanName
参数2:传入的是我们当前正要创建Bean的BeanDefinition
参数3:在这里是直接传入了一个null
具体实现也是比较简单
1.spring先从缓存里面拿一下,看看之前有没有这个BeanName合并过的BeanDefinition,如果有,就先创建一个rootBeanDefinition
的对象,然后直接返回这个root就好了
2.如果没有就要进入合并的步骤了
代码摘自上面的 getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
if (bd.getParentName() == null) {
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}else {
mbd = new RootBeanDefinition(bd);
}
在这个if会先判断当前这个BeanDefiniton有没有parent,如果没有需要将当前BeanDefinition的内容拷贝到新创建的RootBeanfinition里面,然后返回出去即可
如果说有parent进入下面
else {
BeanDefinition pbd;
try {
String parentBeanName = transformedBeanName(bd.getParentName());
if (!beanName.equals(parentBeanName)) {
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {//异常代码省略}
}
}
catch (NoSuchBeanDefinitionException ex) {//异常代码省略}
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);
}
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(SCOPE_SINGLETON);
}
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
if (containingBd == null && isCacheBeanMetadata()) {this.mergedBeanDefinitions.put(beanName, mbd);}
}
这里的代码也不难也很容易阅读,主要做的也就是,获取到当前BeanDefintion的parent,然后将当前bd的内容覆盖parent的内容,然后放入一个rootBeanDefintion进行返,所以基于这个特性我们可以总结到,这里子BeanDefinition是可以覆盖父Beanfinition的属性,也就是说,如果子Beanfinition和父Beanfinition都有A属性,那么后面的生命周期中会以子Beanfinition的A属性为准
除此之外这里还有两点需要注意一下:
1.pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
源码里面有一步递归的操作,这个是为了解决,父BeanDefintion还可能有parent
2.如果当前容器里面没有找到parent,spring还会进入父容器里面去找,如果在父容器里面找到了
也会进行合并,这里不过多介绍父子容器相关的概念
以上就是BeanDefintion的全部内容,进行BeanDefintion合并完成后,就可以进行生命周期的下一步了(后续更新)