【Spring】手动实现Spring底层机制-问题的引出

🎄欢迎来到@边境矢梦°的csdn博文🎄

🎄本文主要梳理手动实现Spring底层机制-问题的引出 🎄
🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈
🎆喜欢的朋友可以关注一下🫰🫰🫰,下次更新不迷路🎆

Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒🌓🌔🌕   

目录

🌸底层难点问题的引出🚀

🌈问题的概念解释

🎈BeanDefinition

🥝类加载器和类路径classPath


🌸底层难点问题的引出🚀

1. 底层如何实现依赖

2. 底层如何实现singleton (单例池)

3. 底层如何实现prototype (多例)

4. 底层如何实现IOC容器创建和初始化(之前用反射实现过, 较浅, 继续深入)

5. 底层如何实现getBean()

6. 底层如何实现BeanPostProcessor

7. Spring底层如何实现Bean后置处理器机制

8. 原生Spring底层如何实现AOP


🌈问题的概念解释

  1. spring底层实现依赖注入(Dependency Injection,DI)的主要机制是通过反射和配置元数据来实现的。依赖注入是Spring框架的核心功能之一,它允许开发者将一个Bean所依赖的其他Bean(依赖项)动态地注入到该Bean中而不需要硬编码这些依赖项。以下是Spring底层如何实现依赖注入的关键步骤:
    1. Bean Definition 注册:在Spring容器初始化的过程中,所有的Bean定义(Bean的类名、属性、依赖等信息)都会被注册到容器的BeanFactory中。这些Bean定义可以通过XML配置文件、Java注解或Java配置类来定义。

    2. 依赖关系定义:在Bean定义中,可以使用不同的方式来定义Bean之间的依赖关系。最常见的方式是使用构造函数注入(Constructor Injection)或Setter方法注入(Setter Injection)。依赖关系可以通过依赖注入的标签或注解来表示。

    3. 依赖解析和注入:当容器需要创建一个Bean实例时,它会检查该Bean的定义,以确定它所依赖的其他Bean。容器会自动解析这些依赖关系,并尝试实例化或获取依赖的Bean。

    4. 依赖注入:容器会将依赖项注入到目标Bean中。这可以通过调用目标Bean的构造函数、Setter方法、字段注入等方式来实现,具体取决于依赖关系的定义方式。

    5. 依赖的生命周期管理:Spring容器负责管理依赖项的生命周期。通常,依赖项的生命周期与目标Bean的生命周期相匹配。如果依赖项是Singleton作用域的Bean,那么它只会被创建一次并在容器启动时初始化;如果是Prototype作用域的Bean,每次注入都会创建一个新的实例。

    6. 可选的AOP代理(可选):如果Bean被配置为使用AOP(面向切面编程),容器可能会为该Bean创建一个代理对象,并将代理对象注入到依赖中,以便在方法调用时应用切面逻辑。

  2. Spring框架实现Singleton模式的核心机制是通过控制Bean的创建和管理来确保在容器中只有一个实例的存在Spring框架使用了一个叫做BeanFactory的容器来管理对象,其中包括了Singleton对象。下面是Spring如何实现Singleton模式的简要说明:

    • 容器的生命周期管理:Spring容器本身也是一个Singleton对象,它负责管理所有的Bean实例。容器的生命周期通常与应用程序的生命周期相匹配。

    • 同步管理:Spring通过使用同步机制来确保在多线程环境下Singleton对象的安全创建和访问。这样可以避免并发问题。

    • 懒加载:虽然Spring默认在容器启动时就创建Singleton对象,但你也可以将Bean的懒加载标志设置为true,以延迟对象的创建直到第一次请求。这样可以节省资源,只有在需要时才会创建Singleton对象。

    • Bean的缓存:Spring使用一个叫做BeanFactory的容器来管理Bean的实例。在BeanFactory内部,Singleton对象会被缓存起来,以确保每次请求同一个Bean时,都会返回相同的实例。

    • Bean的创建和初始化:Spring容器会根据Bean定义创建实例对象。对于Singleton作用域的Bean,容器只会创建一个实例并在容器启动时进行初始化。这个实例会被缓存起来以供后续使用

    • Bean Definition:在Spring配置文件(如XML文件)或者Java配置类中,你需要定义Bean。这个Bean定义包括了Bean的类名、属性、依赖等信息。当Spring容器初始化时,它会解析这些Bean定义。

  3. Spring框架底层实现Prototype作用域的Bean时,采用了一种不同的机制,与Singleton不同。在Prototype作用域下,Spring容器不会维护一个单独的缓存,每次请求Prototype类型的Bean都会创建一个新的实例。下面是Spring如何实现Prototype模式的简要说明:

    • Bean Definition:与Singleton作用域一样,你需要在Spring配置文件或Java配置类中定义Bean。Bean定义包括了Bean的类名、属性、依赖等信息。

    • Bean的创建和初始化:当请求一个Prototype作用域的Bean时,Spring容器会根据Bean定义创建一个新的实例对象,并在需要的情况下进行初始化。每次请求都会触发新的对象创建。

    • 不进行缓存:与Singleton不同,Spring容器不会维护Prototype对象的缓存。每次请求Prototype Bean时,都会创建一个全新的实例。

    • 不进行生命周期管理Spring容器不会管理Prototype对象的生命周期。因此,容器不会在Bean的销毁阶段执行任何清理操作。对象的销毁由客户端代码负责。

  4. 底层实现IOC容器的创建和初始化通常包括以下几个步骤

    • 创建容器对象:实例化一个容器对象,可以是自定义的容器类或者使用现有的容器框
    • 扫描组件:容器会扫描指定的包或者目录,查找符合条件的组件(类)。

    • 注册组件:将扫描到的组件注册到容器中,可以使用反射机制创建实例
    • 解析依赖:对于需要注入的依赖,容器会解析其依赖关系并在需要时注入对应的实例。
    • 初始化组件:对于注册到容器中的组件,容器会调用其相应的初始化方法,可以是在注解上指定的初始化方法或者实现了特定接口的回调方法。
    • 提供访问接口:容器提供相应的接口,如getBean()方法来获取容器中的组件实例。
  5. Spring框架中的getBean()方法是用来从Spring容器中获取Bean实例的主要方法之一。下面是Spring框架底层如何实现getBean()方法的简要说明:

    • Bean Definition的注册:在Spring容器启动过程中,所有的Bean定义(包括Bean的名称、类名、属性等信息)都会被注册到容器的BeanFactory中。这个注册是在容器初始化阶段进行的,通常是通过XML配置文件或Java配置类来完成的。

    • Bean实例化:当调用getBean()方法时,容器首先会根据传入的Bean名称或类型信息,查找相应的Bean定义。

    • Bean的创建和初始化:如果找到了相应的Bean定义,容器会根据定义中的信息创建Bean的实例。这通常涉及到Java反射机制,它会调用Bean的构造函数来创建一个新的对象。然后,容器会应用依赖注入和初始化方法等来配置和初始化这个Bean实例。

    • Bean的缓存:Spring容器通常会维护一个Bean实例的缓存,以确保同一个Bean不会被多次创建。这样,如果之前已经创建了一个Bean实例,下次调用getBean()方法时,容器会直接返回缓存中的实例,而不会再次创建。

    • 返回Bean实例:最终,getBean()方法会返回创建或从缓存中获取的Bean实例,供调用者使用。

    • AOP代理(可选):如果该Bean被配置为使用AOP(面向切面编程),容器可能会为该Bean创建一个代理对象,以便在方法调用时应用切面逻辑。

  6. BeanPostProcessor是Spring框架提供的一个扩展点它允许开发者在Bean初始化的不同阶段插入自定义的逻辑。Spring底层通过一系列的回调方法来实现BeanPostProcessor接口,以便在Bean的初始化前后执行额外的处理。以下是Spring底层如何实现BeanPostProcessor的简要说明:

    • 接口定义BeanPostProcessor是一个接口,其中定义了两个回调方法:

      • postProcessBeforeInitialization(Object bean, String beanName): 在Bean的初始化前执行。在这个方法中,可以对Bean进行修改或者执行一些额外的初始化逻辑。

      • postProcessAfterInitialization(Object bean, String beanName): 在Bean的初始化后执行。在这个方法中,同样可以对Bean进行修改或者执行其他的操作。

    • 注册BeanPostProcessor开发者可以自定义一个或多个实现了BeanPostProcessor接口的类,然后将它们注册到Spring容器中。这可以通过XML配置文件、Java配置类或者注解方式来完成。一旦注册,这些BeanPostProcessor将会被Spring容器调用。

    • 回调方法执行:当Spring容器创建和初始化Bean时,它会在适当的时机调用已注册的BeanPostProcessor的回调方法。具体的调用时机如下:

      • 在Bean的实例化后,但在初始化方法调用之前,会调用postProcessBeforeInitialization方法。

      • 在Bean的初始化方法调用后,会调用postProcessAfterInitialization方法

    • 自定义逻辑:开发者可以在这两个回调方法中编写自己的逻辑,例如,可以在postProcessBeforeInitialization方法中进行属性设置或校验,也可以在postProcessAfterInitialization方法中执行一些清理工作。

      • 在Java中,回调方法(Callback Methods)是一种常见的编程模式,它允许一个对象(通常是一个类)将某个方法的引用传递给另一个对象,以便在特定事件发生时调用该方法。回调方法通常用于事件处理、异步编程和自定义扩展点等情况。

  7. Spring底层实现Bean后置处理器(BeanPostProcessor)机制主要依赖于接口和容器生命周期回调方法。Bean后置处理器允许在容器实例化、配置和初始化Bean之前和之后执行自定义逻辑,例如修改Bean的属性,执行代理等操作。以下是Spring底层如何实现Bean后置处理器机制的简要说明:

    • 接口定义BeanPostProcessor是Spring框架提供的接口,其中包含了两个回调方法:

      • postProcessBeforeInitialization(Object bean, String beanName): 在Bean的初始化之前调用。开发者可以在此方法中修改Bean实例的属性或执行自定义初始化逻辑。

      • postProcessAfterInitialization(Object bean, String beanName): 在Bean的初始化之后调用。开发者可以在此方法中进行清理工作或者对Bean进行后处理。

    • 注册BeanPostProcessor:开发者可以创建自定义的实现了BeanPostProcessor接口的类,并将这些后置处理器注册到Spring容器中。这通常可以通过XML配置文件、Java配置类或注解方式来完成。

    • 容器生命周期回调方法:Spring容器在不同的生命周期阶段会自动调用已注册的Bean后置处理器的回调方法。以下是典型的生命周期回调方法执行顺序:

      • 创建Bean实例。

      • 在调用Bean的构造函数之后,但在初始化方法(如果有的话)之前,调用postProcessBeforeInitialization方法。

      • 在调用Bean的初始化方法之后,调用postProcessAfterInitialization方法。

    • 自定义逻辑:开发者可以在postProcessBeforeInitializationpostProcessAfterInitialization方法中编写自己的逻辑,以根据需要修改、增强或清理Bean。

    • AOP代理(可选):在postProcessBeforeInitialization方法中,开发者可以选择创建AOP代理以包装Bean。这可以用于实现AOP切面。

  8. 原生Spring底层实现AOP(面向切面编程)主要依赖于动态代理和反射机制。Spring使用AOP实现了横切关注点(cross-cutting concerns)的模块化,允许在不改变业务逻辑的情况下,将横切关注点(如日志、事务、安全性检查等)应用于应用程序的各个部分。以下是Spring底层如何实现AOP的简要说明:

    • AOP核心概念

      • 切面(Aspect):切面是包含横切关注点的模块。它定义了何时以及在何地应用横切关注点。

      • 连接点(Join Point):连接点是在应用程序中可能被拦截的点,例如方法调用、异常抛出等。

      • 通知(Advice):通知是在连接点上执行的动作,包括"前置通知"、"后置通知"、"环绕通知"、"抛出异常通知"和"最终通知"。

      • 切点(Pointcut):切点是一组连接点的集合,它定义了哪些连接点应该被拦截。

    • AOP代理生成:Spring底层使用JDK动态代理和CGLIB两种方式生成AOP代理。具体的代理方式取决于目标Bean是否实现了接口。如果目标Bean实现了接口,Spring将使用JDK动态代理,否则将使用CGLIB代理。代理对象负责调用切面的通知方法。

    • 通知执行:当目标方法被调用时,AOP代理会根据切面定义的通知类型,在连接点上执行相应的通知。例如,前置通知会在方法执行前执行,后置通知会在方法执行后执行。

    • 切点匹配:Spring使用切点表达式(Pointcut Expressions)来确定哪些连接点应该被拦截。切点表达式可以根据方法名、包名、类名等条件来匹配连接点。

    • 通知执行顺序:通知可以按照特定的顺序来执行,这个顺序可以在切面配置中定义。通常情况下,通知的执行顺序包括前置通知、环绕通知、后置通知、抛出异常通知和最终通知。

    • XML配置或注解:Spring提供了两种方式配置AOP,一种是通过XML配置文件定义切面、切点和通知,另一种是使用注解来标注切面、切点和通知。


🎈BeanDefinition

在Spring框架中,BeanDefinition是Spring容器中的一个抽象概念,当Spring容器初始化时,它会解析配置文件或者注解,并生成相应的BeanDefinition对象。BeanDefinition是用于描述和定义一个Bean的元数据(数据的数据, 类属性的属性)信息的对象它包含了一个Bean的类名、作用域、属性值、构造函数参数、初始化方法、销毁方法等相关信息。它的主要作用是为容器提供创建和管理Bean实例的必要信息

通过BeanDefinition,Spring容器了解了如何实例化、配置和管理一个Bean对象。它允许我们对Bean进行灵活的配置和定制化可以设置不同的作用域(如单例、原型等),设置属性值,指定构造函数参数等。

BeanDefinition可以通过不同的方式进行定义,包括XML配置文件、注解和Java代码等。无论使用哪种方式,最终都会被解析为BeanDefinition对象,然后由容器根据该对象来创建和管理相应的Bean实例。

Spring的BeanDefinition提供了一个统一的模型来管理和控制Bean的生命周期和行为它使得Spring容器能够根据配置信息动态地创建和管理Bean对象,同时也提供了强大的扩展和定制化能力。

🥝类加载器和类路径classPath

java的类加载器3种

  • Bootstrap类加载器--—----—--—---对应路径jre/lib
  • Ext类加载器---------—----------对应路径jre/lib/ext
  • App类加载器--—-------------—--对应路径classpath

App类加载器常用, classpath 类路径,就是 java.exe 执行时,指定的路径,比如

"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2023.1.2\lib\idea_rt.jar=55328:C:\Program Files\JetBrains\IntelliJ IDEA 2023.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;D:\NewExecllction\exicesecode\lxb-spring\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\5.3.8\spring-context-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\5.3.8\spring-aop-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\5.3.8\spring-beans-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\5.3.8\spring-core-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\5.3.8\spring-jcl-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\5.3.8\spring-expression-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aspects\5.3.8\spring-aspects-5.3.8.jar;C:\Users\Administrator\.m2\repository\org\aspectj\aspectjweaver\1.9.6\aspectjweaver-1.9.6.jar" com.lxb.spring.AppMain

🌰ClassLoader的getResource方法

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

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

相关文章

TypeScript类型兼容:结构化类型

🎬 岸边的风:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 1. 鸭子类型:定义和示例 2. 鸭子类型的优点 2.1 代码的灵活性 2.2 代码的复用 2.3 与 JavaScript 的…

Revit SDK 介绍:TypeRegeneration 修改类型,更新文档 ValidateParameters 参数合法性验证

前言 这篇文章介绍两个例子,逻辑比较简单: TypeRegeneration 修改类型,更新文档ValidateParameters 参数合法性验证 内容 TypeRegeneration FamilyType 不是继承自 Element 的,而是独立于 Element 体系之外,直接从…

RS485(一):电路与波形

一、RS485电路 ​RS485( Recommended Standard-485)是隶属于OSI模型-物理层的电气特性,规定为 2 线、半双工、平衡传输线的多点异步通信标准,通信采用差分信号传输。 典型485应用电路如下图所示: 其中 、# 分别控制接收和发送…

LeetCode 238. 除自身以外数组的乘积

题目链接 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 题目解析 使用前缀和进行解决该题,只不过与之前前缀和不同的是这个题目计算前缀和的时候不需要计算当前元素,也就是当前位置前缀和的值其实是不包含当前元素的前缀和。…

C++内存管理

目录 一.new和delete 二.operator new与operator delete函数 三.new和delete的实现原理 四.在VS2022编译器下new和delete不匹配的问题 五.定位new 六.malloc/free和new/delete的区别 七.C内存分布 八.内存泄漏 C在内存管理上引入了两个操作符,分别是new,de…

Tailwind CSS 速成

Tailwind CSS 速成 完成了 responsive 和特效的学习后,现在折腾一下 tailwind CSS,这个 CSS 库本身就包含了很多的 utility class,之前跟着 yt 的视频写项目的时候,写了两个项目,好像不记得写过 CSS…… Redux Toolk…

SpringMVC的拦截器和JSR303的使用

目录 一、JSR303 二、拦截器(interceptor) 一、JSR303 1.1.什么是JSR303 JSR 303,它是Java EE(现在称为Jakarta EE)规范中的一部分。JSR 303定义了一种用于验证Java对象的标准规范,也称为Bean验证。 Bean验…

Golang goroutine 进程、线程、并发、并行

goroutine 看一个需求 需求:要求统计1-200000000000的数字中,哪些是素数? 分析思路: 1)传统的方法,就是使用一个循环,循环的判断各个数是不是素数(一个任务就分配给一个cpu去做,这样很不划算…

运动耳机哪个好、最好的运动牌子排名榜

很多朋友喜欢在运动的时候听音乐,为此,他们会为自己配备一款蓝牙耳机或是运动耳机,可以在运动的时候随身听,可是,一些人在挑选耳机的时候犯难了,市面上那么多运动耳机,运动耳机哪个好&#xff1…

关于rsync用不了之后

1.尝试找出rsync使用错误原因: 我遇见一个问题:rsync:read errors mapping:communication error on send (70),我查了一下这个问题很大可能是网络链接导致的,然后我用nslookup指令查看了/train2…

测试平台项目部署二(手动部署改成Dockerfile)

测试平台项目部署二(手动部署改成Dockerfile) 一、Dockerfile制作1、entrypoint.sh制作2、构建镜像3、启动容器二、遇到的问题1、pip install --no-cache-dir -r requirements.txt安装第三方库时,报Installing build dependencies: started2、安装第三方库文件比较慢,考虑更…

Vue3自定义指令

文章目录 Vue3自定义指令1. 自定义全局指令v-focus2. 自定义局部指令v-focus3. 指令定义的钩子函数3.1 概念3.2 钩子函数参数3.3 vnode & prevNode3.4 简写3.5 指令函数接受JavaScript表达式 Vue3自定义指令 1. 自定义全局指令v-focus 除了默认设置的核心指令( v-model 和…