Spring IoC容器以及Bean的创建过程
1 Spring 静态结构介绍
什么是Spring,可以自己网上查询,总之,可以说是一个面向Web应用开发的,轻量级,企业级框架。它包含了如下图所示一些模块,这些模块的详细的概念和所负责的职责,可以通过搜索检索到。
1.1 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是否有Singleton,Lazyinit,Depends on属性, Dependency check等特性。BeanDefinition可以通过继承于BeanFactoryPostProcessor的接口的类中来修改。
上图4所示, DefaultSingletonBeanRegistry 用于注册,获得,管理singleton对象。
1.2 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容器的创建调用栈。
2.2 Spring根容器创建的过程
Spring Ioc容器创建的过程,如果直接去跟踪代码的话,是一个非常复杂的过程,很容易陷入进去。下图也是从另外一个博客中直接拷贝的,现在也忘记链接了,希望作者不要介意。从下图,能很明白的看出,重要分为以下4步的执行,完成容器的创建。
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;}
//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的创建过程相关推荐
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...
- Spring IOC容器和Bean的配置
Spring IOC容器 和Bean的配置 : IOC和DI IOC(Inversion of Control):反转控制 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的 ...
- Spring IoC容器与Bean管理18:Bean对象的作用域及生命周期三:对象生命周期;
说明: (1)本篇博客主要根据案例阐述对象的声明周期: (2)其中,比较重要的是注意下这个对应关系: (3)还有就是调用[registerShutdownHook()]销毁IoC容器: 目录 一:be ...
- Spring IoC容器与Bean管理
Spring IoC容器与Bean管理 一.Spring IoC容器与Bean管理 1.Spring快速入门 IoC控制反转 DI依赖注入 Spring概述 Spring IoC初体验 使用XML方式 ...
- [Spring实战系列](6)配置Spring IOC容器的Bean
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/SunnyYoona/article/details/50619900 1. 简介 Spring提供了 ...
- Spring IOC 容器根据Bean 名称或者类型进行autowiring 自动依赖注入
//根据类型对属性进行自动依赖注入 protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWra ...
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
最新文章
- 基于平面几何精确且鲁棒的尺度恢复单目视觉里程计
- 云曦服务器,云曦南羡最新章节_云曦南羡全本小说
- 508人决战,北大占绝对优势,2021阿里全球数学决赛真题发布!
- python列表的复制,扯一下浅拷贝与深拷贝的区别
- TellDontAsk的扩展
- 三丰三坐标编程基本步骤_加工中心开机回零的两种基本方式及常见问题的应对方法...
- BZOJ4017 小Q的无敌异或 好题
- C# winfrom gridview全部选择和全部取消
- Ubuntu 9.10上搭建andrioid开发环境
- python打飞机小程序
- stm32—光敏电阻传感器的初步使用
- C语言排序(冒泡排序、选择排序、插入排序和快速排序)
- aka鉴权 ims_宋月:IMS鉴权过程中各参数的用途
- mininet和ryu简单实现自定义topo
- excel小写转大写公式_知乎高赞回答:Excel从入门到精通,看这一篇就够了!
- 深圳移动实习生面试题
- Windows11镜像下载及安装
- 手把手 | 事理图谱,下一代知识图谱
- Android客户端与本地服务器Socket通信
- AprilTag_ros的使用
热门文章
- 免费Java虚拟主机账号管理思路
- 如何解决远程桌面连接出现内部错误问题?
- PHP多线程pthreads踩坑记
- 开博,2015等待的黎明
- 2021年G2电站锅炉司炉考试题库及G2电站锅炉司炉找解析
- python paramiko_Python2.7 paramiko模块
- 软件测试ios打包,ios开发怎样将打包ipa发送给测试人员
- Camera2实现带照相框的可修改显示效果的自定义照相机
- jdbi和jdbc_数据库中间件-jdbi
- 如何将excel表格导入word_「超百科0341」如何批量导入excel数据到指定格式的word表格?...