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.mzph.cn/news/592517.shtml

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

相关文章

奇因子之和(C语言)

题意&#xff1a; 一个整数的因子&#xff0c;就是所有可以整除这个数的数。奇数指在整数中&#xff0c;不能被 2 整除的数。所谓整数 Z 的奇因子&#xff0c;就是可以整除 Z 的奇数。 给定 N 个正整数&#xff0c;请你求出它们的第二大奇因子的和。当然&#xff0c;如果该数只…

Amazon API Gateway CORS 实战

Amazon API Gateway提供了一种实现跨域资源共享&#xff08;CORS&#xff09;的方式&#xff0c;以便在Web应用程序中安全地使用API。下面是Amazon API Gateway CORS的实战指南&#xff1a; 创建一个API Gateway REST API并定义资源和方法。在资源上启用CORS&#xff0c;可以通…

程序的重定位

可以理解为编译和链接 过程中产生的地址项都是临时的相对的。编译的时候的地址&#xff0c;在链接时会被修改。最终链接后生成的bin文件的地址项&#xff0c;在加载运行时 也会被修改。 链接器会对所有的输入文件进行扫描&#xff0c;之后就可以确定段的大小&#xff0c;符号定…

从0开始搭建清华ChatGLM3 6b大模型(Windows RTX4090版)

目录 1、硬件及软件说明 2、安装Anaconda 3、安装Git版本控制 ​4、安装pytorch驱动 5、安装ChatGLM3 1、硬件及软件说明 硬件&#xff1a;主要是GPU卡内存要足够&#xff0c;本次搭建使用的RTX4090卡一张&#xff0c;单卡内存24G&#xff0c;为什么选择4090&#xff1f;…

如何在ArcGIS Pro中指定坐标系

在进行制图的时候&#xff0c;为了实现某些特定的效果&#xff0c;需要指定特定的坐标系&#xff0c;但是现有的数据可能不是所需要的坐标系&#xff0c;这时候就需要对现有的数据坐标系进行处理&#xff0c;这里为大家介绍一下ArcGIS Pro中指定坐标系的方法&#xff0c;希望能…

ECMAScript和JavaScript:深入理解它们的关系与区别

ECMAScript和JavaScript&#xff1a;深入理解它们的关系与区别 在讨论ECMAScript和JavaScript之间的关系及其区别时&#xff0c;我们首先需要澄清一些常见的误解。很多人会将这两个术语混为一谈&#xff0c;但实际上&#xff0c;它们指代的是不同的概念。今天&#xff0c;我们…

STM32存储左右互搏 SPI总线读写FRAM MB85RS2M

STM32存储左右互搏 SPI总线读写FRAM MB85RS2M 在中低容量存储领域&#xff0c;除了FLASH的使用&#xff0c;&#xff0c;还有铁电存储器FRAM的使用&#xff0c;相对于FLASH&#xff0c;FRAM写操作时不需要预擦除&#xff0c;所以执行写操作时可以达到更高的速度&#xff0c;其…

蓝牙物联网漏洞攻击的几种方式?

在物联网日益普及的今天&#xff0c;蓝牙技术的广泛应用为我们的生活带来了诸多便利。然而&#xff0c;正如一枚硬币有两面&#xff0c;蓝牙技术的普及也带来了新的安全挑战。近日&#xff0c;一项关于蓝牙物联网漏洞攻击的研究引起了广泛关注。这项研究揭示了蓝牙物联网所面临…

机器视觉在食品安全检测领域的应用与展望

​随着人们生活水平的提高&#xff0c;对食品安全的要求也越来越高。在这种背景下&#xff0c;机器视觉技术作为一种高效、准确的自动化检测手段&#xff0c;在食品安全检测领域扮演着越来越重要的角色。机器视觉系统通过模拟人眼的视觉功能&#xff0c;借助相机和计算机视觉算…

魅族手机怎么录屏?高清视频,轻松录制!

“有人知道魅族手机怎么录屏吗&#xff0c;新买的魅族手机&#xff0c;用了几天感觉挺流畅的&#xff0c;功能也很齐全&#xff0c;最近因为工作原因&#xff0c;需要用到录屏功能&#xff0c;但是我不知道怎么打开&#xff0c;就想问问大家&#xff0c;魅族手机怎么录屏呀。”…

【qt】保存debug到log里

新建一个log.h #ifndef LOG_H #define LOG_H#include <QFile> #include <QTextStream> #include <QDateTime> #include <QMutex> #include <QDir>//选择屏幕打印还是输出到文件可以根据这个宏控制或者控制函数调用位置都可以 //#define _DEBUG …

ARM CCA机密计算架构软件栈简介

本博客描述了Arm机密计算架构(Arm CCA)的固件和软件组件。 在这篇博客中,您将学到如何: 列出组成Arm CCA软件栈的组件集了解Arm CCA引入新软件组件的原因了解监视器和领域管理监视器(RMM)的角色了解如何创建和管理领域1.1 开始之前 我们假设您熟悉AArch64异常模型、AAr…

2024 年 8 款值得收藏的免费 Android 数据恢复软件

如果你发现手机数据全部被删除&#xff0c;先别慌&#xff0c;今天这个视频就来教你如何恢复。 随着市场上数据恢复软件的可用性不断增加&#xff0c;很难选择哪一款是最好的。今天&#xff0c;我们精心挑选了8个最佳免费Android数据恢复软件。他们肯定会帮助你决定最适合你需…

JAVA对象、List、Map和JSON之间的相互转换

JAVA对象、List、Map和JSON之间的相互转换 1.Java中对象和json互转2.Java中list和json互转3.Java中map和json互转 1.Java中对象和json互转 Object obj new Object(); String objJson JSONObject.toJSONString(obj);//java对象转json Object newObj JSONObject.parseObject(…

ECMAScript和JavaScript的区别

ECMAScript 和 JavaScript 之间的区别在于它们各自的定义和用途&#xff1a; ECMAScript 定义&#xff1a; ECMAScript 是 JavaScript 语言的规范。 它是由 Ecma 国际组织的 TC39 委员会开发的标准化脚本语言规范。 作用&#xff1a; ECMAScript 定义了脚本语言的语法、类型、…

引导过程的解析以及教程za

bios加电自检------mbr--------grub-------加载内核文件------启动第一个进程 bios的主要作用&#xff1a;检测硬件是否正常&#xff0c;然后根据bios中的启动项设置&#xff0c;去找内核文件 boot开机启动项顺序&#xff0c;你可以把内核文件放在何处&#xff1f; 1.硬盘 …

Python分支语句

if 语句 if用来根据一个条件判断是否要执行某段逻辑代码&#xff0c;语法是&#xff1a; if 条件:pass这里的条件指的是通过条件运算符和逻辑运算符组成的一个条件表达式&#xff0c;或者就是一个布尔值。有些特殊的使用场景下&#xff0c;也可以直接跟变量名&#xff0c;遵循…

CMake入门教程【基础篇】条件语句(if)

文章目录 if语句语法比较运算符字符串比较文件和目录判断变量判断布尔表达式正则表达式匹配函数和宏判断 示例应用比较运算符示例字符串比较示例文件和目录判断示例变量判断示例布尔表达式示例正则表达式匹配示例函数和宏判断示例 结论 #mermaid-svg-LGW4z5n9jNvtC9OT {font-fa…

ChatGPT 进行 SEO的使用技巧

搜索引擎优化 (SEO) 是使网站对搜索引擎友好的一种不断发展的实践。 自搜索引擎和新兴技术的发展以来&#xff0c;它从未保持不变。 最近发布的 ChatGPT 是一种人工智能对话工具&#xff0c;似乎在搜索引擎优化方面有很好的应用。 从创建吸引人的标题到只需一个简短的提示就可…

QML 中自定义虚拟键盘

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 我们知道 Qt 中虚拟键盘模块遵循的是 GPL 协议&#xff0c;是不可用于商业发布的。如果项目中使用了 Qt 自带的虚拟键盘&#xff…