Spring技术内幕笔记之IOC的实现

IOC容器的实现

依赖反转:

依赖对象的获得被反转了,于是依赖反转更名为:依赖注入。许多应用都是由两个或者多个类通过彼此的合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用,如果这个获取过程需要自身实现,那么这将导致代码高度耦合并且难以测试。 ----维基百科

依赖控制反转有很多实现方式。在Spring中,IOC容器是实现这个模式的载体,它可以在对象生成或初始化时直接将数据注入到对象中,也可以通过对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。 
 

IOC容器系列的设计与实现:BeanFactory 和 ApplicationContext

Spring IOC容器的设计中,主要两个主要的容器系列:

一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本的功能;

另一个是ApplicationContext应用上下文,他作为容器的高级形态而存在。

具体什么是容器呢?它在Spring框架中到底长什么样?

其实对IOC容器的使用者来说,我们经常接触到的BeanFactory 和 ApplicationContext都可以看成是容器具体表现形式。ApplicationContext是BeanFactory的子接口,BeanFactory提供了配置和基本功能,ApplicationContext添加了更多企业特定的功能,ApplicationContext是BeanFactory的完整超集。在Spring中,Spring有各式各样的IOC容器的实现供用户选择和使用。

ApplicationContext接口表示Spring IOC容器,负责实例化、配置和组装bean。容器通过读取配置元数据来获取实例化、配置和组装对象的指令。配置元数据用XML、Java注解或Java代码表示。它允许组成应用程序的对象以及这些对象之间丰富的相互依赖关系。

Spring提供了几个ApplicationContext接口的实现。在独立应用程序中,通常创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。虽然XML一直是定义配置元数据的传统格式,但可以通过提供少量XML配置以声明式地启用对这些附加元数据形式的支持,来指示容器使用Java注解或代码作为元数据格式

BeanDefinition

Spring通过BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。IOC容器是用来管理对象依赖关系的,对IOC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。

Spring IOC容器的设计

BeanFactory的应用场景

BeanFactory接口定义了IOC容器最基本的形式,并且提供了IOC容器所应该遵守的最基本的服务契约。

BeanFactory主要接口:

public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;boolean containsBean(String name);boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class targetType) throwsNoSuchBeanDefinitionException;Class getType(String name) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}

ApplicationContext的应用场景

ApplicationContext是一个高级形态意义的IOC容器,ApplicationContext在BeanFactory的基础上添加了很多附加功能。

IOC容器的初始化过程

简单来说,IOC容器的初始化是由refresh()方法来启动的。主要包括:

  1. resource定位过程 桶装水,先找到水

  2. BeanDefinition载入 水处理成合格的水

    相当于把定义的BeanDefinition在IOC容器中转化成一个Spring内部表示的数据结构的过程。IOC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关相关操作来完成的。这些BeanDefinition数据在IOC容器中通过一个HashMap来保持和维护。

    refresh()方法执行过程

    prepareRefresh();
    //这里是在子类中启动refreshBeanFactory()的地方
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);try {//设置BeanFactoy的后置处理postProcessBeanFactory(beanFactory);//调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的invokeBeanFactoryPostProcessors(beanFactory);//注册Bean的后处理器,在Bean创建过程中调用。registerBeanPostProcessors(beanFactory);//对上下文中的消息源进行初始化initMessageSource();//初始化上下文中的事件机制initApplicationEventMulticaster();//初始化其他的特殊BeanonRefresh();//检查监听Bean并且将这些Bean向容器注册registerListeners();//实例化所有的(non-lazy-init)单件finishBeanFactoryInitialization(beanFactory);//发布容器事件,结束Refresh过程finishRefresh();} catch (BeansException ex) {//为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件BeandestroyBeans();}
    
  3. 向IOC容器注册这些BeanDefinition 将水装入桶中

    将BeanDefinition放入值beanDefinitionMap Map中

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

总结:IOC容器的初始化过程主要是工作是在IOC容器中建立BeanDefinition数据映射

==注意:Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器搜索Bean的时候(除了layzinit设置为true,设置了lazyinit属性那么这个Bean的依赖注入在IOC容器初始化时就预先完成了无需等待初始化完成后使用getBean去触发)。==

补充:lazy-init属性的处理,其实是直接采用getBean去触发依赖注入。所以说白了,与正常依赖注入的出发相比,只是触发的时间和场合不同,原理是一样的。

IOC容器的依赖注入

一句话:getBean是依赖注入的起点,之后会调用createBean,createBean不仅生成了Bean,还会对Bean初始化进行处理。比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。

主要涉及到方法:

  1. org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean // 获取Bean

  2. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean // 创建Bean

  3. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance // 创建Bean实例

    提供了两种实例化Java对象方法,一种通过使用Java反射功能(BeanUtils.instantiateClass),一种是通过CGLIB生成。

    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// Don't override the class with CGLIB if no overrides.if (!bd.hasMethodOverrides()) {Constructor<?> constructorToUse;synchronized (bd.constructorArgumentLock) {constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;if (constructorToUse == null) {final Class<?> clazz = bd.getBeanClass();if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {if (System.getSecurityManager() != null) {constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);}else {constructorToUse = clazz.getDeclaredConstructor();}bd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}return BeanUtils.instantiateClass(constructorToUse);}else {// Must generate CGLIB subclass.return instantiateWithMethodInjection(bd, beanName, owner);}}
    
  4. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean // 填充Bean

  5. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) // Bean的初始化方法调用

    通过jdk反射机制得到Method对象,然后调用Bean定义中申明的初始化方法

  6. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods 执行Bean初始化方法

    1. 如果Bean实现了InitializingBean接口,则先执行afterPropertiesSet()方法

    2. 执行Bean的init方法(Bean定义的initMethodName)

IOC容器中Bean生命周期

  1. Bean实例的创建
  2. 为Bean实例设置属性
  3. 调用Bean的初始化方法
  4. 应用使用Bean
  5. Bean销毁

FatoryBean的实现

通过org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean方法得知,获取Bean核心是factory.getObject(),也就说通过实现FactoryBean接口,开放getOject方法,供使用者自由发挥使用。

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {Object object;try {if (System.getSecurityManager() != null) {AccessControlContext acc = getAccessControlContext();try {object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);}catch (PrivilegedActionException pae) {throw pae.getException();}}else {object = factory.getObject();}}catch (FactoryBeanNotInitializedException ex) {throw new BeanCurrentlyInCreationException(beanName, ex.toString());}catch (Throwable ex) {throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);}// Do not accept a null value for a FactoryBean that's not fully// initialized yet: Many FactoryBeans just return null then.if (object == null) {if (isSingletonCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");}object = new NullBean();}return object;}

可看出此时getBean是通过getObject()方法获取到的Bean

BeanPostProcessor的实现

简称“BPP” ,BPP是使用IOC容器时经常使用到的一个特性,这个Bean的后置处理器是一个监听器,可以监听容器触发的事件。将它向IOC容器注册后,容器中管理的Bean具备了接收IOC容器事件回调的能力。BPP是一个接口,主要只有以下两个方法:

public interface BeanPostProcessor {/***  在Bean的初始化前提供回调入口*/@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}/***  在Bean的初始化后提供回调入口*/@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) 初始化Bean

如何与IOC结合?

autowiring(自动依赖装配)的实现

只有在类变量属性为类,此时需要在IOC容器中寻找依赖的对象,由此可推出自动依赖装配的过程是在属性填充过程中即populateBean()方法中实现。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

在populateBean方法中可找到autowireByName与autowireByType两个方法,通过类名/类型进行自动装配

autowireByName通过名称自动注入,直接根据属性名getBean()从IOC容器中获取Bean即可。

autowireByType稍微复杂一些,但是最终依然是通过getBean从IOC容器中获取Bean。

参考:

Spring技术内幕:深入解析Spring架构与设计原理(第2版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/316534.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql的读写分离

MySQL 读写分离原理 读写分离就是只在主服务器上写&#xff0c;只在从服务器上读。 主数据库处理事务性操作&#xff0c;而从数据库处理 select 查询。 数据库复制被用来把主数据库上事务性操作导致的变更同步到集群中的从数据库。 常见的mysql读写分离分为以下两种 1&…

贝叶斯推断:细谈贝叶斯变分和贝叶斯网络

1. 贝叶斯推断 统计推断这件事大家并不陌生&#xff0c;如果有一些采样数据&#xff0c;我们就可以去建立模型&#xff0c;建立模型之后&#xff0c;我们通过对这个模型的分析会得到一些结论&#xff0c;不管我们得到的结论是什么样的结论&#xff0c;我们都可以称之为是某种推…

day5--java基础编程:异常,内部类

6 异常 6.1 异常概述 出现背景&#xff1a; 在使用计算机语言进行项目开发的过程中&#xff0c;即使程序员把代码写得尽善尽美&#xff0c;在系统的运行过程中仍然会遇到一些问题&#xff0c;因为很多问题不是靠代码能够避免的&#xff0c;比如:客户输入数据的格式&#xff0c…

React使用动态标签名称

最近在一项目里&#xff08;React antd&#xff09;遇到一个需求&#xff0c;某项基础信息里有个图标配置&#xff08;图标用的是antd的Icon组件&#xff09;&#xff0c;该项基础信息的图标信息修改后&#xff0c;存于后台数据库&#xff0c;后台数据库里存的是antd Icon组件…

QT上位机开发(会员管理软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了ini文件的解析办法&#xff0c;通过QSettings类就可以很轻松地访问ini文件里面的数据。除了ini文件之外&#xff0c;另外一种经常出…

将 Python 和 Rust 融合在一起,为 pyQuil® 4.0 带来和谐

文章目录 前言设定方向从 Rust 库构建 Python 软件包改装 pyQuil异步困境回报&#xff1a;功能和性能结论 前言 pyQuil 一直是在 Rigetti 量子处理单元&#xff08;QPUs&#xff09;上构建和运行量子程序的基石&#xff0c;通过我们的 Quantum Cloud Services&#xff08;QCS™…

Ansible、Slatstack、Puppet自动化运维工具介绍

一、自动化运维工具的选择 1、为什么要用自动化运维工具&#xff1f; 运维的痛点: 海量的设备越来越多&#xff0c;每台设备单独管理需要投入更多人力&#xff1b; 传统运维效率低&#xff0c;大多工作人为完成&#xff1b; 传统运维工作繁琐&#xff0c;人工操作容易出错…

搭建宠物寄养小程序流程

近日&#xff0c;一地宠物寄养需求旺盛&#xff0c;元旦满房&#xff0c;春节几近饱和&#xff0c;一窝难求。随着市场需求的增长&#xff0c;对于很多宠物行业的商家&#xff0c;可以考虑开展宠物寄养服务&#xff0c;尤其是节假日的宠物寄养需求会更高。因此&#xff0c;商家…

uniapp中uview组件库的Input 输入框 的使用方法

目录 #平台差异说明 #基本使用 #输入框的类型 #可清空字符 #下划线 #前后图标 #前后插槽 API #Props #Events #Methods #Slots 去除fixed、showWordLimit、showConfirmBar、disableDefaultPadding、autosize字段 此组件为一个输入框&#xff0c;默认没有边框和样式…

东信免驱系列身份证阅读器串口通讯协议解析示例,适用于单片机、ARM等系统开发集成使用

完整的一次读卡流程包括&#xff1a; 身份证寻卡 > 身份证选卡 > 身份证读卡&#xff0c;三个步骤 缺一不可&#xff08;见通讯协议&#xff09;。 寻卡&#xff1a;EA EB EC ED 04 00 B0 B4 BB 返回&#xff1a;EA EB EC ED 05 00 00 B0 B5 BB 选卡&#xff1a;EA …

script标签 async 、defer区别

script标签 async 、defer区别 先上图&#xff1a; 1.普通的javascript note:普通JS的下载和解析都会影响DOM解析 2.async note:js的下载不影响DOM&#xff0c;执行影响DOM 在执行到加了async的script时会先下载&#xff0c;然后再去执行下一个标签。待到这个script的外…

计算机组成原理 存储器概述,主存系统模型和RAM和ROM

文章目录 存储器概述基本概念存储器层次结构存储器分类性能指标 主存系统模型和结构存储元结构主存寻址 RAM和ROMRAM概念RAM对比DRAM刷新集中刷新分散刷新异步刷新 ROM 存储器概述 #mermaid-svg-EjCg9aMsdPUw7lra {font-family:"trebuchet ms",verdana,arial,sans-se…