前奏

  最近看了一系列解析spring管理Bean的源码的文章,在这里总结下,方便日后复盘。文章地址https://www.cnblogs.com/CodeBear/p/10336704.html

  spring的一大核心就是Ioc,即把Bean放到一个公共的容器中,既替开发者管理了Bean的生命周期,又解耦了类之间的持有关系。

  spring中,管理Bean的容器都叫xxxContext,这里的继承关系有点复杂,但是功能是相同的--存放并管理各种Bean。我们常用的用注解生成的Bean,都放在AnnotationConfigApplicationContext类型的容器里。所以,程序启动时一定会执行如下语句:

AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);

  生成一个AnnotationConfigApplicationContext容器实例,用来存放通过注解生成的Bean。具体包括:@Configuration,@Component,@Import,@Resouce,@Service,@ComponentScan等注解。所以重点是AnnotationConfigApplicationContext这个类。

AnnotationConfigApplicationContext类

  其构造方法只有三行代码,长这样:

//根据参数类型可以知道,其实可以传入多个annotatedClasses,但是这种情况出现的比较少
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//调用无参构造函数,会先调用父类GenericApplicationContext的构造函数
//父类的构造函数里面就是初始化DefaultListableBeanFactory,并且赋值给beanFactory
//本类的构造函数里面,初始化了一个读取器:AnnotatedBeanDefinitionReader read,一个扫描器ClassPathBeanDefinitionScanner scanner
//scanner的用处不是很大,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
this();
//把传入的类进行注册,这里有两个情况,
//传入传统的配置类
//传入bean(虽然一般没有人会这么做
//看到后面会知道spring把传统的带上@Configuration的配置类称之为FULL配置类,不带@Configuration的称之为Lite配置类
//但是我们这里先把带上@Configuration的配置类称之为传统配置类,不带的称之为普通bean
register(annotatedClasses);
//刷新
refresh();
}

参数是多个Class对象,表示要实例化的Bean的类型,一般情况只传一个。

this():做的是初始化操作,生成几个初始的、必要的Bean。比如AnnotatedBeanDefinitionReader等一系列Bean,用来读取代码中注解信息。

register(annotatedClasses) :将传入的参数,目标Bean的类型经过一些判断后(是否满足Condition、单例还是其他作用域、是否懒加载)注册进来,主要工作是将名字及类型信息注册到DefaultListableBeanFactory中的Map中。

refresh() :由于第二步新注册了目标Bean的Class,因此要刷新,通过反射生成上一步新添加的类型的Bean。

第一步:this() 初始化

1. AnnotatedBeanDefinitionReader

   this()调用此类的无参构造方法:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    //注解bean定义读取器,主要作用是用来读取被注解的了bean
private final AnnotatedBeanDefinitionReader reader; //扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
private final ClassPathBeanDefinitionScanner scanner; /**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
//会隐式调用父类的构造方法,初始化DefaultListableBeanFactory //初始化一个Bean读取器
this.reader = new AnnotatedBeanDefinitionReader(this); //初始化一个扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
}

可以看到,这个方法初始化了AnnotatedBeanDefinitionReader,字面意思就可以看出这玩意是读取代码中要通过注解生成的Bean的信息(包括名字、类型、作用域、条件等),因此参数直接把this传入,直接把信息读到本容器中。

2. DefaultListableBeanFactory

另外,this()中又会自动调用父类GenericApplicationContext的无参构造:

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
}

可以看到,GenericApplicationContext这个父类实例化了一个DefaultListableBeanFactory,所以作为继承者的AnnotationConfigApplicationContext也就有了DefaultListableBeanFactory实例。从名字就可以看出这是一个生产Bean的工厂。Bean工厂的结构主要是一个Map以BeanName为key,BeanDefinition为value,其中BeanDefinition记录了此Bean的类型、是否懒加载、作用域等信息;还有List,存放每个Bean的名字。要通过这个工厂生成一个Bean时,就需要把目标Bean的名字、具体信息传递给工厂,如下:

//beanDefinitionMap是Map<String, BeanDefinition>,
//这里就是把beanName作为key,ScopedProxyMode作为value,推到map里面
this.beanDefinitionMap.put(beanName, beanDefinition);
//beanDefinitionNames就是一个List<String>,这里就是把beanName放到List中去
this.beanDefinitionNames.add(beanName);

3. Reader和Factory的合作

  Reder是读取Bean信息,Factory保存有Map、List从而保存了Bean信息,所以二者肯定会在这件事上协作。实际上,也确实如此,Reader构造时可是以Context为参数传入的,Context又持有Factory,因此Reader可以很轻易的把读取到的信息保存到Factory。中间的过程、函数调用跳来跳去,反正最终调用:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
//点进去这个方法,如下:⬇⬇⬇⬇⬇⬇
---------------------------------------------------
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);

要注意的是:

this()这一行是初始化内置的Bean,Reader会通过ConfigurationClassPostProcessor类的Bean是否已经存在容器中判断是不是初始化;调用this()时是初始化,容器中还没有PostProcessor的Bean,所以Reader也不会去读取代码中的Bean,而是由 RootBeanDefinition的构造方法获得ConfigurationClassPostProcessor的BeanDefinition(RootBeanDefinition是BeanDefinition的子类,表示内置Bean的信息),将这个PostProcessor的Bean信息存到Factory中。ConfigurationClassPostProcessor作为内置Bean当然有大用。

this()这一行总结就是:

  (1) 初始化了一个AnnotatedBeanDefinitionReader,以本容器为构造参数,读取代码中注解信息,把要生成的Bean的信息读到本容器中;

  (2)  从父类继承来DefaultListableBeanFactory,存放读进来的Bean的信息。里面有Map以BeanName为key,BeanDefinition为value,其中BeanDefinition记录了此Bean的类型、是否懒加载、作用域等信息;还有List,存放每个Bean的名字。

  (4) Reader将ConfigurationClassPostProcessor的Bean先注册到Factory中。

  (3) 容器之所以为容器就是因为持有一个Factory可以根据名字得到具体信息从而实例化一个Bean返回,其中Factory的原料又通过Reader读进来。

第二步:register(annotatedClasses)  注册Bean的类型

  初始情况下,只注册有几个spring的内置Bean,当通过注解要在容器中生成用户自己的Bean时则要调用此代码将目标Bean的Class注册进去,再刷新从而让DefaultListableBeanFactory得到更新。这里贴一下原文步骤:

    1. 通过AnnotatedGenericBeanDefinition的构造方法,获得配置类的BeanDefinition,这里是不是似曾相似,在注册ConfigurationClassPostProcessor类的时候,也是通过构造方法去获得BeanDefinition的,只不过当时是通过RootBeanDefinition去获得,现在是通过AnnotatedGenericBeanDefinition去获得。

    2. 判断需不需要跳过注册,Spring中有一个@Condition注解,如果不满足条件,就会跳过这个类的注册。

    3. 然后是解析作用域,如果没有设置的话,默认为单例。

    4. 获得BeanName。

    5. 解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description。

    6. 限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上是任何注解,这里没有判断注解的有效性)。

    7. 把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中(这个不是很重要,可以简单的理解为方便传参)。

    8. 注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册:

Spring源码--Bean的管理总结(一)的更多相关文章

  1. spring源码-bean之增强初始化-3

    一.ApplicationContext的中文意思是“应用上下文”,它继承自BeanFactory接口,除了包含BeanFactory的所有功能之外,在国际化支持.资源访问(如URL和文件).事件传播 ...

  2. spring源码-bean之初始化-1

    一.spring的IOC控制反转:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了松耦合.当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查 ...

  3. spring源码-bean之加载-2

    一.前面说了bean的容器初始化,后面当然是说bean的加载.这里还是不讲解ApplicationContext的bean的加载过程,还是通过最基础的XmlBeanFactory来进行讲解,主要是熟悉 ...

  4. Spring源码-Bean生命周期总览

  5. spring源码-自定义标签-4

    一.自定义标签,自定义标签在使用上面相对来说非常常见了,这个也算是spring对于容器的拓展.通过自定义标签的方式可以创造出很多新的配置方式,并且交给容器直接管理,不需要人工太多的关注.这也是spri ...

  6. spring源码-开篇

    一.写博客也有一段时间了,感觉东西越来越多了,但是自己掌握的东西越来越少了,很多时候自己也在想.学那么多东西,到头来知道的东西越来越少了.是不是很奇怪,其实一点都不奇怪. 我最近发现了一个很大的问题, ...

  7. Spring源码剖析依赖注入实现

    Spring源码剖析——依赖注入实现原理 2016年08月06日 09:35:00 阅读数:31760 标签: spring源码bean依赖注入 更多 个人分类: Java   版权声明:本文为博主原 ...

  8. spring源码-Aware-3.4

    一.Aware接口,这个也是spring的拓展之一,为啥要单独拿出来讲呢,因为他相比于BeanFactoryPostProcessor,BeanPostProcessor的实用性更加高,并且在具体的业 ...

  9. spring源码-BeanPostProcessor-3.3

    一.BeanPostProcessor这个是spring容器的拓展之一,是用于获取bean的时候处理对应的对象: 二.常用场景,在获取bean的时候,重新初始化bean的属性等. 三.实现方式(加入容 ...

随机推荐

  1. 使用 go protoc --go_out 输出的 *.pb.go文件时报 undefined: proto.ProtoPackageIsVersion3

    事情是这样的:我参考go的 grpc 实现 https://grpc.io/docs/quickstart/go/ Download the example The grpc code that wa ...

  2. MySQL单列索引和组合索引的创建及区别介绍

    MySQL单列索引是我们使用MySQL数据库中经常会见到的,MySQL单列索引和组合索引的区别可能有很多人还不是十分的了解,下面就为您分析两者的主要区别,供您参考学习. 为了形象地对比两者,再建一个表 ...

  3. Java ——运算符

    本节重点思维导图 递增递减 前缀自增自减法(++a,--a): 先进行自增.减运算,再进行表达式运算 后缀自增自减法(a++,a--): 先进行表达式运算,再进行自增.减运算 例[1]: int a ...

  4. LeetCode链表简单题

    一.21合并两个有序链表 代码如下: class Solution: def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNod ...

  5. 将python 2.6 升级到 2.7,及pip安装

    由于CentOS6.5 自带python版本为2.6.6,实际中使用的大多为2.7.x版本.于是手动升级. 查看python的版本 #python -VPython 2.6.6 1.下载Python- ...

  6. [转帖]怎样选择(FC-SAN)光纤通道(存储)交换机

    怎样选择(FC-SAN)光纤通道(存储)交换机 https://blog.csdn.net/sinat_30171789/article/details/50510936 交换机的种类非常多...   ...

  7. Tomcat&Servlet笔记

    # 今日内容     1. web相关概念回顾     2. web服务器软件:Tomcat     3. Servlet入门学习 ## web相关概念回顾     1. 软件架构         1 ...

  8. python中django中间件

    一.中间件 所谓的中间件,就是存在socket和视图函数中间的一种相当于过滤的机构. 中间件共分为: (1)process_request(self,request) (2)process_view( ...

  9. 高德地图搜索功能以及清除搜索结果maker

    第一次写文章,写得不好各位看官见谅~ (pσ_σ)P首先这是一个vue里面的项目,高德地图api是直接CDN进来的,所以使用了global来调用,默认已经初始化了一个地图,为了实现一个输入框搜索功能和 ...

  10. Vue.nextTick浅析

    Vue.nextTick浅析 Vue的特点之一就是响应式,但数据更新时,DOM并不会立即更新.当我们有一个业务场景,需要在DOM更新之后再执行一段代码时,可以借助nextTick实现.以下是来自官方文 ...