Spring源码--Bean的管理总结(一)

前奏

  最近看了一系列解析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():做的是初始化操作,BeanDefinitionRegistry的对象是注册中心,根据它的信息生成几个初始的、必要的Bean。

register(annotatedClasses) :将传入的参数,目标Bean的类型注册进来,即注册到BeanDefinitionRegistry对象里去。

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

第一步:this() 初始化

  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传入。  另外,this()中又会自动调用父类GenericApplicationContext的无参构造:

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

可以看到,GenericApplicationContext这个父类实例化了一个DefaultListableBeanFactory,所以作为继承者的AnnotatedBeanDefinitionReader也就有了DefaultListableBeanFactory实例。从名字就可以看出这是一个生产Bean的工厂。Bean工厂的原料在哪里呢?我们需要初始化这个工厂的信息。这里根据BeanDefinitionRegistry的注册信息,生成一个Map<name,definition>作为工厂的原料:

 private static BeanDefinitionHolder registerPostProcessor(
            BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {  

        definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(beanName, definition);
        return new BeanDefinitionHolder(definition, beanName);
    }
--------------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
        this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
    }
--------------------
//beanDefinitionMap是Map<String, BeanDefinition>,
//这里就是把beanName作为key,ScopedProxyMode作为value,推到map里面
this.beanDefinitionMap.put(beanName, beanDefinition);
//beanDefinitionNames就是一个List<String>,这里就是把beanName放到List中去
this.beanDefinitionNames.add(beanName);

this()这一行总结就是:

  (1) 初始化了一个AnnotatedBeanDefinitionReader,以本容器为构造参数,读取本容器放的Bean;

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

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

第二步: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方法去注册:

原文地址:https://www.cnblogs.com/shen-qian/p/11721276.html

时间: 2024-10-18 21:25:49

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

spring源码-bean之初始化-1

一.spring的IOC控制反转:控制反转--Spring通过一种称作控制反转(IOC)的技术促进了松耦合.当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象.说白了就是原本需要手动去创建和管理的对象,现在交给容器了.在容器的基础上,实现对象的控制.这个方式也很好的减少了开发者在框架上面的时间,更多关注点在业务逻辑上面. 二.这表博客的主要目的是研究,bean容器的初始化,会在最基本的bean初始化作源码的解析和分析.如何存在不妥当的地方,还请

spring源码-bean之加载-2

一.前面说了bean的容器初始化,后面当然是说bean的加载.这里还是不讲解ApplicationContext的bean的加载过程,还是通过最基础的XmlBeanFactory来进行讲解,主要是熟悉通过BeanFactory是怎么实现class的实例化的. 二.声明一下:XmlBeanFactory和ApplicationContext的不同在与,ApplicationContext在容器初始化的时候,就做了很多准备操作.而XmlBeanFactory是在获取bean的时候才会调用初始化的东西

spring源码-开篇

一.写博客也有一段时间了,感觉东西越来越多了,但是自己掌握的东西越来越少了,很多时候自己也在想.学那么多东西,到头来知道的东西越来越少了.是不是很奇怪,其实一点都不奇怪. 我最近发现了一个很大的问题,就是自己做开发这么久了.很多原理以及实现都不是很了解,包括自己在假装面试了解自己不足的时候,也突显除了自己的问题所在.面试官会问你,你真的懂这个技术吗.那么它的实现原理是怎么样的,简单的描述一下.当时我就懵逼了,这个问题说严重也严重,说不严重也不严重. 因为,作为2年开发的我.开发和自己积累下来的应

spring源码-自定义标签-4

一.自定义标签,自定义标签在使用上面相对来说非常常见了,这个也算是spring对于容器的拓展.通过自定义标签的方式可以创造出很多新的配置方式,并且交给容器直接管理,不需要人工太多的关注.这也是spring对于配置拓展的一个很重要的方式. 二.自定义标签的几个步骤:1.创建可扫描的标签和对应的解析类  2.读取页面元素解析 3.加入容器管理 三.涉及到的常用类:BeanDefinitionParser.NamespaceHandlerSupport:文件:spring.handlers.sprin

spring源码深度解析— IOC 之 开启 bean 的加载

概述 前面我们已经分析了spring对于xml配置文件的解析,将分析的信息组装成 BeanDefinition,并将其保存注册到相应的 BeanDefinitionRegistry 中.至此,Spring IOC 的初始化工作完成.接下来我们将对bean的加载进行探索. 之前系列文章: spring源码深度解析— IOC 之 容器的基本实现 spring源码深度解析— IOC 之 默认标签解析(上) spring源码深度解析— IOC 之 默认标签解析(下) spring源码深度解析— IOC

看看Spring的源码——Bean加载过程

最近几天跟同事聊起Spring的一些问题,对一些地方有些疑问,趁这两天有点空,看看Spring的源码,了解下具体的实现细节.本文基于Spring 4.0.5版本. 首先Web项目使用Spring是通过在web.xml里面配置org.springframework.web.context.ContextLoaderListener初始化IOC容器的. <listener> <listener-class>org.springframework.web.context.ContextL

【Spring源码分析】非懒加载的Bean实例化过程(下篇)

doCreateBean方法 上文[Spring源码分析]非懒加载的Bean实例化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下AbstractAutowireCapableBeanFactory的doCreateBean方法代码: 1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[]

【Spring源码分析】Bean加载流程概览

代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已,Spring的加载过程相对是不太透明的,不太好去找加载的代码入口. 下面有很简单的一段代码可以作为Spring代码加载的入口: 1 ApplicationContext ac = new Clas

Spring源码阅读:Spring事务管理的基础

上一节了解了全局事务与局部事务以及Spring提供的两种事务模式:编程式事务与声明式事务. 不论是编程式的事务处理,还是声明式的事务处理.他们都要对局部事务和全局事务以支持,也就是说要对JDBC进行支持.ORM框架,同时也要对JTA进行支持.他们的公共部分是commit,rollback.通过这一节的了解,我相信以后配置Spring事务时,就不需要在去网上查资料了或者去查Spring的参考文档了. 因此,Spring设计了如下的事务管理框架: 从上面的类图中和容易可以看出分为三部分:Platfo