🎄欢迎来到@边境矢梦°的csdn博文🎄
🎄本文主要梳理手动实现Spring底层机制-问题的引出 🎄
🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈
🎆喜欢的朋友可以关注一下🫰🫰🫰,下次更新不迷路🎆
Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒🌓🌔🌕
目录
🌸底层难点问题的引出🚀
🌈问题的概念解释
🎈BeanDefinition
🥝类加载器和类路径classPath
🌸底层难点问题的引出🚀
1. 底层如何实现依赖
2. 底层如何实现singleton (单例池)
3. 底层如何实现prototype (多例)
4. 底层如何实现IOC容器创建和初始化(之前用反射实现过, 较浅, 继续深入)
5. 底层如何实现getBean()
6. 底层如何实现BeanPostProcessor
7. Spring底层如何实现Bean后置处理器机制
8. 原生Spring底层如何实现AOP
🌈问题的概念解释
- spring底层实现依赖注入(Dependency Injection,DI)的主要机制是通过反射和配置元数据来实现的。依赖注入是Spring框架的核心功能之一,它允许开发者将一个Bean所依赖的其他Bean(依赖项)动态地注入到该Bean中,而不需要硬编码这些依赖项。以下是Spring底层如何实现依赖注入的关键步骤:
-
Bean Definition 注册:在Spring容器初始化的过程中,所有的Bean定义(Bean的类名、属性、依赖等信息)都会被注册到容器的BeanFactory中。这些Bean定义可以通过XML配置文件、Java注解或Java配置类来定义。
-
依赖关系定义:在Bean定义中,可以使用不同的方式来定义Bean之间的依赖关系。最常见的方式是使用构造函数注入(Constructor Injection)或Setter方法注入(Setter Injection)。依赖关系可以通过依赖注入的标签或注解来表示。
-
依赖解析和注入:当容器需要创建一个Bean实例时,它会检查该Bean的定义,以确定它所依赖的其他Bean。容器会自动解析这些依赖关系,并尝试实例化或获取依赖的Bean。
-
依赖注入:容器会将依赖项注入到目标Bean中。这可以通过调用目标Bean的构造函数、Setter方法、字段注入等方式来实现,具体取决于依赖关系的定义方式。
-
依赖的生命周期管理:Spring容器负责管理依赖项的生命周期。通常,依赖项的生命周期与目标Bean的生命周期相匹配。如果依赖项是Singleton作用域的Bean,那么它只会被创建一次并在容器启动时初始化;如果是Prototype作用域的Bean,每次注入都会创建一个新的实例。
-
可选的AOP代理(可选):如果Bean被配置为使用AOP(面向切面编程),容器可能会为该Bean创建一个代理对象,并将代理对象注入到依赖中,以便在方法调用时应用切面逻辑。
-
-
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定义。
-
-
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的销毁阶段执行任何清理操作。对象的销毁由客户端代码负责。
-
-
底层实现IOC容器的创建和初始化通常包括以下几个步骤
- 创建容器对象:实例化一个容器对象,可以是自定义的容器类或者使用现有的容器框
- 扫描组件:容器会扫描指定的包或者目录,查找符合条件的组件(类)。
- 注册组件:将扫描到的组件注册到容器中,可以使用反射机制创建实例。
- 解析依赖:对于需要注入的依赖,容器会解析其依赖关系并在需要时注入对应的实例。
- 初始化组件:对于注册到容器中的组件,容器会调用其相应的初始化方法,可以是在注解上指定的初始化方法或者实现了特定接口的回调方法。
- 提供访问接口:容器提供相应的接口,如getBean()方法来获取容器中的组件实例。
-
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创建一个代理对象,以便在方法调用时应用切面逻辑。
-
-
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)是一种常见的编程模式,它允许一个对象(通常是一个类)将某个方法的引用传递给另一个对象,以便在特定事件发生时调用该方法。回调方法通常用于事件处理、异步编程和自定义扩展点等情况。
-
-
-
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
方法。
-
-
自定义逻辑:开发者可以在
postProcessBeforeInitialization
和postProcessAfterInitialization
方法中编写自己的逻辑,以根据需要修改、增强或清理Bean。 -
AOP代理(可选):在
postProcessBeforeInitialization
方法中,开发者可以选择创建AOP代理以包装Bean。这可以用于实现AOP切面。
-
-
原生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方法