Spring之实例化Bean(2)

Spring是非常复杂的一个框架,想要一篇博客就说完实例化Bean的全流程,那将注定会是是很长很长的篇博客,说实话,换做我自己是没有耐心看完的。

本章,我将会以一个最简单的例子来阐述Spring实例化Bean的过程,下一章我们将会重点基于本章节的实例化过程,来阐释一些复杂的实例化过程。 比如:@Autowired @Resource如何注册的,以及循环依赖的大流程。

在开始今天的源码分析之前,我们需要强调几个概念,不懂没关系,先记住就好:

首先,getBean(beanName)方法就是实例化bean的方法,而且是核心方法。

其次, spring中会存在1级、2级、3级缓存的概念。1级缓存是存放已经实例化好的bean的,2级缓存和3级缓存都是存放正在实例化过程中的bean的相关信息的。

接着上一篇Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客,我们知道,在spring开始实例化之前,我们都是需要首先把java类封装成BeanDefinition对象的。

今天,我们看一下本章测试的最简单的Demo

package com.xiangxue.jack.bean;

import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;

@Repository
public class Dao {

    private String name;

    private String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @PostConstruct  //相当于init-method
    void init () {
        id = "001";
        name = "yy";
    }
    @Override
    public String toString() {
        return "name :" + name + " id :" + id;
    }
}

今天,我就从这个最简单的类实例化开始将起。

 1. 首先进入AbstractApplicationContext的refresh方法中,我们直接跳转到实例化bean的方法处

 进入这个方法 :

 继续进入:

此时,我们见到了熟悉的面孔了。beanDefitionNames这个集合是放所有的beanDenation的name值的,然后我们根据name获取到BeanDefinition对象。 此时,我们知道,只有是单例、非抽象、非懒加载的BeanDefinition才有资格继续进行实例化操作

2. 此时,代码继续往下走,我们发现它调用了getBean方法,而本章节一开始我就说过,getBean方法就是实例化Bean的。因此,此时应该是进入了实例化bean的主流程了,逐层debug进去:

3. getBean(beanName)方法内部调用了doGetBean方法进行实例化的,继续跟进去,我们发现它是从缓存中拿的。而第一次实例化之前,肯定是拿不到任何bean的对象的。

此处,我们有必要点进去看一下getSingleton方法,看一下3级缓存。其实,就是3个集合而已。此处稍微记一下就好,本章节不会就3级缓存展开解释。

4. 此时,我们接着 doGetBean方法继续主干流程讲解。中间会有父子类合并,@DependOn的判断等等,全部跳过。我们直接来到实例化bean的核心代码处

 此时,我们需要关注2个方法,分别是getSingleton方法和createBean方法。这是一个lambda表达式, 流程就是先调用getSingleton方法,然后回调createBean方法

getSingleton:首先从1级缓存中拿,拿不到就把当前的beanDefinition的名字放入集合  singletonsCurrentlyInCreation中,表示当前beanDefinition正在实话bean;

 然后调用singletonObject = singletonFactory.getObject(); 进入到createBean方法

 

createBean : 在createBea方法中,我们会调用到doCreateBean方法, 然后在doCreateBean内部会调用到createBeanInstance方法

 

此时,我们进入到createBeanInstance方法中:其实,在这个方法中,我们知道实例化的2种方法,一个是有参构造函数,另一个是无参构造函数。而本章节的demo,我们是最简单的实例化步骤,因此是无参构造函数

 

 到此时,我们知道最终返回值是一个经过包装的BeanWrapper对象。但是,我们继续debug进去看实例化的具体步骤:

继续跟进,我们知道就是反射调用生成对象了

反射实例化完成以后,我们将对象包装成BeanWrapper对象进行返回。

5. 此时,我们还是回到了doCreateBean方法体中,继续往下走。

我们会遇到方法applyMergedBeanDefinitionPostProcessors,此处我们稍微介绍一下。这个方法其实就是搜集注解的作用。比如:@Autowired @Resource @PostConstruct等等。

这些收集注解的工作,是我们在实例化BeanDefinition的时候,通过调用registerComponents方法注进去的各种PostProcessor实现的。上一章的补充说明部分。可以直接在Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客

中搜索 “最后补充一下”。

继续回到applyMergedBeanDefinitionPostProcessors方法:

 此处,我仅仅列出了一个CommonAnnotationBeanPostProcessor作为举例。

6. 言归正传,我们还是继续doCreateBean方法, 往下走:接下来我们会遇到3个比较重要的方法,逐个列举出来解释:

addSingletonFactory : 添加缓存

populateBean: ioc di,依赖注入的核心方法。 本章是最简单的实例化过程,不涉及。下章节会重点分析。

initializeBean:实例化+ioc依赖注入完以后的调用,简单概括就是对象实例化完成以后,里面的变量,无论是通过注解注入的,还是调用什么方法初始化的,此时都没有值。本章中,我们使用到了@PostConstruct注解,那么我们此处简单的分析一下这个方法。下章节再重点分析。

进入这个方法,我们直接来到核心代码处: 

 点进去,我们发现,它又是调用我们在实例化BeanDefinition是注册的那些PostProcessor。我们使用到了@PostConstruct注解,那么我们还是以CommonAnnotationBeanPostProcessor为例进行分析:

 进入CommonAnnotationBeanPostProcessor的postProcessBeforeInitialization方法:

 这里,我们先来解释一下MetaData类。每个java都会生成一个BeanDefinition和一个MetaData对象,而MetaData里面存储了类的基本信息,比如:这个类的父类是谁,使用了哪些注解,有哪些方法,类和方法的作用域等各种各样的信息。MetaData会被大量的使用,以后我们会经常看到。

继续跟进这个方法:

此时,在我们的Dao.java文件的init方法上打上断点,代码会直接进入我们的方法进行初始化操作。因为它使用了@PostConstruct注解:

   @PostConstruct  //相当于init-method
    void init () {
        id = "001";
        name = "yy";
    }

返回到 applyBeanPostProcessorsBeforeInitialization方法中,我们可以确认dao对象已经被初始化完成:

继续返回上一层方法 initializeBean 中。此时,下面还有2个比较重要的方法,本例暂时没有用到,暂时忽略。

继续返回上一层方法中,此时由来到了doCreateBean方法中了。此时,它会注册bean销毁时的类DisposableBeanAdapter,最后返回bean对象。

继续返回上一层方法中,此时来到了createBean方法中。

等等,前面好像说过createBean方法,从第4步开始介绍createBean,一直到现在才最终回到了createBean方法中,而且这个方法内部又调用了N多实例化的核心方法,值得反复研究。此时,createBean方法来到了结尾处了:

返回createBean方法的上一层:

 我们之前说过,在调用getSingleton方法的时候会回调进入createBean方法,那此时createBean方法结束了,接下来又会进入到getSingleton方法中了

继续debug方法 getSingleton 往下走:此时会完成2件事

第4步一开始的时候,我们说从一级缓存中拿不到beanName的对象,会把beanName放入一个集合中,表示这个bean正在实例化。而此时,我们实例化和初始化都完成了,需要把当前的beanName从表示正在创建的集合中删除掉。

第4步一开始的时候就说从缓存中拿beanName的对象,那时候拿不到。现在已经完成实例化了。需要将当前实例化的bean放入1级缓存,并从2、3级缓存中删除。

 

 哇塞,说了这么多,终于完成了第4步全部流程的梳理。就这么一小段代码,反反复复的调用,干了如此多的事情,真是不容易啊

	if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					//改方法是FactoryBean接口的调用入口
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

7. 好了,我们又回到了主流程方法 doGetBean 中了。接下来就是一路返回。至此,实例化Bean对象也已经全部完成了。

测试:

下面开始我们的测试

package com.xiangxue.jack;

import com.xiangxue.jack.bean.Dao;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;

//@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml"})
public class MyTest {

    @Autowired
    private ApplicationContext applicationContext;

   
    @Test
    public void myBean3() {
        applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Dao dd = (Dao) applicationContext.getBean("dao");
        System.out.println(dd.toString());
    }
}

直接debug 进入getBean方法中,只要我们从1级缓存中直接拿到对象就说明整个流程梳理都是对的。

 确实是拿到了实例化并且初始化后的bean对象了,而且控制台也正确的打印了出来。测试成功

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值