1 Spring 静态结构介绍

什么是Spring,可以自己网上查询,总之,可以说是一个面向Web应用开发的,轻量级,企业级框架。它包含了如下图所示一些模块,这些模块的详细的概念和所负责的职责,可以通过搜索检索到。

1.1 BeanFactory

BeanFactory关键类继承

上图1所示位置, BeanFactory是整个BeanFactoryl类体系中最顶层的接口。

上图2所示, HierachicalBeanFactory  描述了BeanFactory是可以组成一个链表结构。使用名字等信息去查找Bean的时候,当子级BeanFactory内没有找到相应的Bean,可以去父级BeanFactory内去查找。这样可以节省框架内Bean占用的内存。

上图3所示, ListableBeanFactory 是存放Bean的列表接口,可以直接将所有的Bean枚举出来,而不是通过BeanFactory接口中,通过名字等信息来查询Bean。

上图6所示, DefaultListableBeanFactory 实现了BeanDefinitionRegister接口;在DefaultListableBeanFactory中,首先可以看到保存beanDefinition名字和对象映射beanDefinitionMap的一个Map中;另外,所有beanDefinition的名字保存到一个列表中beanDefinitionNames

/** Map of bean definition objects, keyed by bean name */

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

/** List of bean definition names, in registration order */

private final List<String> beanDefinitionNames = new ArrayList<String>(64);

另外,BeanDefinition是用来描述Bean的元信息的类,内部包含了Bean是否有SingletonLazyinitDepends on属性, Dependency check等特性。BeanDefinition可以通过继承于BeanFactoryPostProcessor的接口的类中来修改。

上图4所示, DefaultSingletonBeanRegistry  用于注册,获得,管理singleton对象。

1.2 ApplicationContext

ApplicationContext继承图

AbstractApplicationContext从上图可以看出,是整个继承体系中,第一个类(抽象类),是整个容器的核心处理类,此类内部有模板函数fresh(模板设计模式),在此函数内,约定容器启动的步骤。子类们可以实现具体特化的启动步骤。另外,注意, 在此类中,有抽象函数getBeanFactory函数,用来获取真正的BeanFactory实例。而AbstractApplicationContext也继承了BeanFactory的接口,而AbstractApplicationContext实现BeanFactory的接口是通过代理getBeanFactory获取的工厂实例实现的,是一个标准的代理设计模式。

     @Overridepublic Object getBean(String name) throws BeansException {return getBeanFactory().getBean(name);}

public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

ClassPathXmlApplicationContext生成的beanFactory的实例,是DefaultListableBeanFactory的对象。此实例,保存在AbstractRefreshableApplicationContext类中。

AbstractApplicationContext中的BeanFactory接口的代理设计模式的实现实例如下,

后续我们在研究整个容器创建过程也是通过refresh这个函数为主线,进行展开的。

2 Spring动态创建简介

2.1 Spring中的多级容器概念

在第一小节中,介绍了HierachicalBeanFactory 接口,说明Spring的容器可以是一个级链为一个列表,并且子容器可以复用父级容器内的Bean;

接下来,我们查看Web.xml文件;一般来说,一共生成了两个容器,一个是ROOT_WEB_APPLICATION_CONTEXT容器,另一个容器是SpringMVC容器,SpringMVC容器的父容器是ROOT_WEB_APPLICATION_CONTEXT容器。

ROOT_WEB_APPLICATION_CONTEXT根容器是通过ContextLoaderListener类生成的,这个类的调用时因为继承了ServletContextListener接口的,所以,ContextLoaderListener会在创建完ServletContext后,进行调用,这个时刻,所有的Servlet还未创建。因为,要在这个地方来初始化Servlet公用的一些对象,例如数据库相关的对象,以备后续的Servlet来调用。

<!--第一个容器: Spring上下文监听器配置,生成根容器 --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!--DispatcherServlet MVC容器 --><servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:context-dispatcher.xml</param-value></init-param></servlet>

我们查看ContextLoadListener调用栈,能明显的看到ROOT_WEB_APPLICATION_CONTEXT容器的创建调用栈。

ServletContextListener初始化Spring容器调用栈

2.2  Spring根容器创建的过程

Spring Ioc容器创建的过程,如果直接去跟踪代码的话,是一个非常复杂的过程,很容易陷入进去。下图也是从另外一个博客中直接拷贝的,现在也忘记链接了,希望作者不要介意。从下图,能很明白的看出,重要分为以下4步的执行,完成容器的创建。

容器内Bean创建过程

1 创建BeanFactory,之后,将BeanDefinition注册到BeanFactory的实例中,最后将这些信息保存在上文描述的beanDefinitionMap中,并且将名字也保存到beanDefinitionNames的数组中。这里要说明的是,Spring中的Bean的创建信息,是以BeanDefinition类来描述的。内部描述当前Bean是否是单例Bean,是否延时初始化,依赖属性,依赖值类型检测等属性。

2 对第一步创建的BeanDefinition进行修改;继承于BeanFactoryPostProcessor接口的类,实例化后,要对第一步生成的BeanDefinition实例的属性进行修改。在下图中的第二部invokeBeanFactoryPostProcessors函数中,调用这些接口,用来修改BeanDefinition的  某些特性;

3 实例化并注册继承于BeanPostProcessor接口的类;在registerBeanPostProcessors的函数中,会实例化Bean的后处理器。这些后处理器,是继承于BeanPostProcessor接口的子类,是针对初始化完毕的Bean实例进行一些属性的修改(这里要注意,继承于BeanFactoryPostProcessor的类是用来修改BeanDefinition的属性,而继承于BeanPostProcessor的类是用来修改Bean的。这两类接口统称为PostProcessor后处理,主要是为了统一修改BeanDefinition和Bean的)。这些需要修改的Bean,通常是通过通过注释或者接口,来标记这些需要修改的Bean。Spring中,一个非常重要的应用AOP,就是在靠这些继承于BeanPostProcessor接口的类来进行处理的。

4 创建Bean

在finishBeanFactoryInitialization函数中,初始化Bean。从以下代码中,能看出,容器的初始化过程refresh函数内,函数的调用过程。

2.3 Bean初始化过程

此章节,将概要的介绍2.2中第四步的初始Bean流程。所以,可见Bean和new出来的实例对象,完全不是一个概念。Bean生成的过程更复杂,只有完成了所有的初始化过程,才是合格的SpringBean。

1 循环在之前步骤中生成的beanDefinitionNames数组;

2 查看每个BeanDefinition,是否有依赖属性需要实例化,需要的话,先实例所有需要实例化的依赖属性

3 利用反射原理,实例化Bean实例

4 将依赖属性注入到Bean中;

5 执行初始化过程,内部会调用后处理器(继承于BeanPostProcessor的类),以及一些初始化接口

接下来将通过代码,概要介绍下,Bean的生成过程;由于DefaultListableFactory实例保存了BeanDefinition的对象Map,和名字数组beanDefinitionNames,所有,首先就是循环这个beanDefinitionNames列表,在循环体内,会调用getBean来尝试的获取Bean。

 public void preInstantiateSingletons() throws BeansException {List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //初始化非延时加载的单例Bean... ...省略getBean(beanName);}}}

进入getBean函数,可以看到,程序会最终调用到AbstractBeanFactory中的doGetBean函数中。我们只看主流程,获取名字对应的BeanDefinition对象,检查是否有依赖属性,如果有,就循环创建依赖属性的Bean,然后,通过createBean来创建Bean。

//AbstractBeanFactory protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {... ... 省略final String beanName = transformedBeanName(name);final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 是否有依赖属性,如果有,循环创建依赖属性的BeanString[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dependsOnBean : dependsOn) {registerDependentBean(dependsOnBean, beanName);getBean(dependsOnBean);}}// 创建Beanif (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return createBean(beanName, mbd, args);}});}... ...省略}

继续查看 createBean的源代码,最后能看到最终调用的是doCreateBean函数,在此函数中,首先通过调用createBeanInstance来实例化Bean;感兴趣可以自己去查看细节,主要还是通过反射原理来实例化类,具体的实例化细节,不会影响对整个过程的理解。

   protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {// Instantiate the bean.BeanWrapper instanceWrapper = null;//... ... 省略if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}// Initialize the bean instance.Object exposedObject = bean;//填充依赖属性populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}// ... ... 省略return exposedObject;}
接下来查看,initializeBean这个函数。这个函数很重要,对Bean实例后实例的一系列修改处理,有通过Bean继承的接口进行修改的,有通过PostProcessor后处理器实例进行修改;具体,可以查看以下代码,一共有三个重要步骤,1 PostProcessor的后处理器的postProcessBeforeInitialization接口函数调用 2 如果当前Bean实现了InitializingBean接口,那么afterPropertiesSet函数将会被调用 3 postProcessor的后处理器的postProcessAfterInitialization接口函数调用。
//AbstractAutowareCapableBeanFactory
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {//... ...省略Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {//PostProcessor的后处理器的postProcessBeforeInitialization接口函数调用wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}//... ...省略//如果当前Bean实现了InitializingBean接口,那么afterPropertiesSet函数将会被调用invokeInitMethods(beanName, wrappedBean, mbd);
//... ...省略if (mbd == null || !mbd.isSynthetic()) {//PostProcessor的后处理器的postProcessAfterInitialization接口函数调用wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); }return wrappedBean;
}

这里要注意,PostProcessor也可以理解为,是Spring框架给了我们一个可以对Bean做一切处理一个机会。例如,ApplicationContextAwareProcessor这个后处理器,这个处理器是内置的后处理器。但是,这个类是为了当前Bean如果实现了某些特定的接口,那么这个后处理,可以去调用这些接口方法,来初始化当前Bean。例如,ApplicationContextAware接口,可以使我们获取ApplicationContext容器对象,具体见如下代码。至此,一个合格的Bean就创建完毕。

class ApplicationContextAwareProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {//省略invokeAwareInterfaces(bean);return bean;}private void invokeAwareInterfaces(Object bean) {if (bean instanceof Aware) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}// 如果当前Bean实现了此接口,将会被调用if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}}
}

Spring IoC容器以及Bean的创建过程相关推荐

  1. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  2. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  3. Spring IOC容器和Bean的配置

    Spring IOC容器 和Bean的配置 : IOC和DI IOC(Inversion of Control):反转控制 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的 ...

  4. Spring IoC容器与Bean管理18:Bean对象的作用域及生命周期三:对象生命周期;

    说明: (1)本篇博客主要根据案例阐述对象的声明周期: (2)其中,比较重要的是注意下这个对应关系: (3)还有就是调用[registerShutdownHook()]销毁IoC容器: 目录 一:be ...

  5. Spring IoC容器与Bean管理

    Spring IoC容器与Bean管理 一.Spring IoC容器与Bean管理 1.Spring快速入门 IoC控制反转 DI依赖注入 Spring概述 Spring IoC初体验 使用XML方式 ...

  6. [Spring实战系列](6)配置Spring IOC容器的Bean

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/SunnyYoona/article/details/50619900 1. 简介 Spring提供了 ...

  7. Spring IOC 容器根据Bean 名称或者类型进行autowiring 自动依赖注入

    //根据类型对属性进行自动依赖注入 protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWra ...

  8. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  9. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

最新文章

  1. 基于平面几何精确且鲁棒的尺度恢复单目视觉里程计
  2. 云曦服务器,云曦南羡最新章节_云曦南羡全本小说
  3. 508人决战,北大占绝对优势,2021阿里全球数学决赛真题发布!
  4. python列表的复制,扯一下浅拷贝与深拷贝的区别
  5. TellDontAsk的扩展
  6. 三丰三坐标编程基本步骤_加工中心开机回零的两种基本方式及常见问题的应对方法...
  7. BZOJ4017 小Q的无敌异或 好题
  8. C# winfrom gridview全部选择和全部取消
  9. Ubuntu 9.10上搭建andrioid开发环境
  10. python打飞机小程序
  11. stm32—光敏电阻传感器的初步使用
  12. C语言排序(冒泡排序、选择排序、插入排序和快速排序)
  13. aka鉴权 ims_宋月:IMS鉴权过程中各参数的用途
  14. mininet和ryu简单实现自定义topo
  15. excel小写转大写公式_知乎高赞回答:Excel从入门到精通,看这一篇就够了!
  16. 深圳移动实习生面试题
  17. Windows11镜像下载及安装
  18. 手把手 | 事理图谱,下一代知识图谱
  19. Android客户端与本地服务器Socket通信
  20. AprilTag_ros的使用

热门文章

  1. 免费Java虚拟主机账号管理思路
  2. 如何解决远程桌面连接出现内部错误问题?
  3. PHP多线程pthreads踩坑记
  4. 开博,2015等待的黎明
  5. 2021年G2电站锅炉司炉考试题库及G2电站锅炉司炉找解析
  6. python paramiko_Python2.7 paramiko模块
  7. 软件测试ios打包,ios开发怎样将打包ipa发送给测试人员
  8. Camera2实现带照相框的可修改显示效果的自定义照相机
  9. jdbi和jdbc_数据库中间件-jdbi
  10. 如何将excel表格导入word_「超百科0341」如何批量导入excel数据到指定格式的word表格?...