【Spring源码解读!底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨

本文主要是介绍【Spring源码解读!底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 🎉🎉欢迎光临🎉🎉

🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀

🌟特别推荐给大家我的最新专栏《Spring 狂野之旅:底层原理高级进阶》 🚀

本专栏纯属为爱发电永久免费!!!

这是苏泽的个人主页可以看到我其他的内容哦👇👇

努力的苏泽icon-default.png?t=N7T8http://suzee.blog.csdn.net/

✨这篇文分了三部分来解释Spring--BeanFactory以及ApplicationContext内部的工作原理 本来想一文到底的 奈何太长了...于是拆成了两篇文  

本篇文章的目标,在于通过深入剖析Spring框架的源码,不仅让读者亲身体验到Spring设计大师们的卓越智慧,同时也分享我对其设计哲学所引发的思考与感悟。

如果 觉得阿泽写的还过得去的 观众老爷们可以给个免费的三连 作为一位纯粹的技术热爱者为爱发电的动力🌟

目录

🌼 引言

第一章:Spring框架概览

🌱 Spring框架的设计哲学

在日常工作中的好处

示例

🌿 核心组件与它们的协同工作方式

第二章:深入BeanFactory —— Spring的基石

BeanFactory的角色与职责

定义与类型

核心接口解析

Bean生命周期管理

Bean定义的解析过程

Bean的实例化与依赖注入

初始化与销毁回调

案例分析:BeanFactory如何管理Bean

Spring源码解析

实例化

依赖注入

初始化

测试代码

第三章:ApplicationContext —— 超越BeanFactory

ApplicationContext与BeanFactory的关系

ApplicationContext的核心功能

国际化消息处理

事件发布与监听机制

环境抽象与配置文件管理

实现原理揭秘

如何实现Bean的自动装配

AOP支持的背后机制

🌼 引言

欢迎来到Spring的世界:简介与动机

我很荣幸能与大家一起探索Spring框架的奇妙之处。本文将带领大家从BeanFactory到ApplicationContext,一步步揭示Spring框架的设计原理和核心组件之间的协同工作方式。在这个过程中,我将结合代码和源码解读,以及适当的测试代码来证明观点的正确性。

第一章:Spring框架概览

🌱 Spring框架的设计哲学

Spring框架的设计哲学体现了延迟加载和依赖注入的思想。通过延迟加载,Spring框架可以在应用程序运行时根据需要动态创建和加载Bean,而不是在启动时就创建所有的Bean。这种设计思想有助于提高应用程序的性能和资源利用率。

具体来说,当我们通过BeanFactory获取Bean的时候,Spring框架会检查该Bean是否已经被创建。如果该Bean尚未被创建,Spring框架会按照配置文件中的定义,使用合适的策略来实例化和初始化该Bean。这种延迟加载的设计思想使得应用程序能够更加灵活地管理和使用组件。

在日常工作中的好处

延迟加载和依赖注入的设计思想在日常工作中有许多好处,其中包括:

  1. 提高性能和资源利用率:由于Spring框架延迟加载的特性,只有在需要使用某个Bean时才会进行创建和加载。这可以减少启动时间、内存占用和网络传输等开销,从而提高应用程序的性能和资源利用率。

  2. 降低耦合度:Spring框架的依赖注入机制使得组件之间的依赖关系由外部容器来管理,而不是硬编码在代码中。这样可以降低组件之间的耦合度,使得代码更加灵活、可维护和可测试。

  3. 增强扩展性和可配置性:通过使用Spring框架,我们可以将应用程序的配置信息集中管理,而不是散落在各个组件中。这使得我们可以更方便地进行配置修改和扩展,而无需修改代码。

示例

以下是一个简单的示例,展示了延迟加载和依赖注入在实际业务场景中的提升:

// 示例:订单服务
@Service
public class OrderService {@Autowiredprivate PaymentService paymentService;public void placeOrder() {// 业务逻辑省略// 调用支付服务完成支付paymentService.pay();// 业务逻辑省略}
}// 示例:支付服务
@Service
public class PaymentService {public void pay() {// 执行支付逻辑System.out.println("执行支付逻辑");}
}

在上述示例中,订单服务依赖于支付服务。通过使用Spring框架的依赖注入机制,我们可以将支付服务注入到订单服务中,而无需在订单服务中硬编码创建和管理支付服务的实例。

这样,当订单服务需要使用支付服务时,Spring框架会自动创建和注入支付服务实例。同时,由于延迟加载的特性,支付服务只会在需要时才会被创建和加载。这种设计使得代码更加灵活、可维护和可测试。

🌿 核心组件与它们的协同工作方式

Spring框架的核心组件就像大自然中的各种元素,它们相互配合、相互作用,才能构建出一个完整的生态系统。让我们通过代码和源码解读的方式来展示它们之间的协同工作方式。

// 核心组件的协同工作
public class SpringEcoSystem {public void coordinateWork() {// 创建ApplicationContext对象ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 从ApplicationContext中获取BeanFactoryBeanFactory beanFactory = context.getBeanFactory();// 使用BeanFactory创建和管理BeanComponentA componentA = beanFactory.getBean(ComponentA.class);ComponentB componentB = beanFactory.getBean(ComponentB.class);// 省略其他代码}
}

在上述代码中,我们使用了Spring的ApplicationContext来创建一个应用程序的上下文,并通过它获取了BeanFactory对象。BeanFactory是Spring框架中的一个核心组件,负责创建和管理Bean。

通过源码解读,我们可以发现BeanFactory的设计思想是延迟加载和依赖注入。它在需要使用Bean时才去创建它,而不是在应用程序启动时就创建所有的Bean。这种延迟加载的设计思想可以提高应用程序的性能和资源利用率。

接下来,我们通过测试代码来证明BeanFactory的延迟加载特性:

// 测试BeanFactory的延迟加载特性
public class BeanFactoryLazyLoadingTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取BeanFactory对象BeanFactory beanFactory = context.getBeanFactory();// 执行一些其他操作for (int i = 0; i < 10000; i++) {System.out.println("test");}// 获取ComponentA对象ComponentA componentA = beanFactory.getBean(ComponentA.class);System.out.println("获取ComponentA对象");// 执行一些其他操作for (int i = 0; i < 10000; i++) {System.out.println("test");}// 获取ComponentB对象ComponentB componentB = beanFactory.getBean(ComponentB.class);System.out.println("获取ComponentB对象");}
}
// 预期结果和执行时间估计
获取ComponentA对象
test
test
test
...
test
获取ComponentB对象

在上述测试代码中,我们先获取了BeanFactory对象,然后执行了一些其他操作。在需要使用ComponentA和ComponentB时,才去通过BeanFactory来获取它们。这样就实现了Bean的延迟加载。

结果是,在执行"获取ComponentA对象"和"获取ComponentB对象"之前,不会创建和加载这两个组件。只有当真正需要使用它们时,才会进行创建和加载。

通过以上的代码和测试,我们可以看到Spring框架中的BeanFactory的设计思想:

延迟加载和依赖注入。

  1. 延迟加载:通过先获取BeanFactory对象,然后执行其他操作的方式,实现了Bean的延迟加载。在需要使用ComponentA和ComponentB时,才去通过BeanFactory来获取它们。这意味着只有在真正需要使用这些组件时,才会进行创建和加载。延迟加载的特性可以提高应用程序的性能和资源利用率。

  2. 依赖注入:通过获取BeanFactory对象,并在需要使用ComponentA和ComponentB时通过BeanFactory来获取它们,实现了依赖注入。在示例中,ComponentA和ComponentB被注入到其他组件中,而不是在代码中硬编码创建和管理它们的实例。这种依赖注入的设计思想降低了组件之间的耦合度,使得代码更加灵活、可维护和可测试。

第二章:深入BeanFactory —— Spring的基石

在Spring框架中,BeanFactory是一个非常核心的接口,它为Spring容器的基础功能提供了定义。BeanFactory可以看作是一个高级的工厂模式实现,负责配置、创建和管理应用程序中的beans。这篇博客将深入探讨BeanFactory的角色、职责和关键实现细节。

BeanFactory的角色与职责

BeanFactory是Spring IoC容器的心脏,IoC(控制反转)是一种设计原则,用于实现依赖注入(DI)。通过BeanFactory,我们可以获得对Spring容器管理的对象(即Beans)的完全控制。它负责:

  • 创建和管理应用程序中的bean
  • 解决bean之间的依赖
  • 管理bean的生命周期
定义与类型

在Spring中,BeanFactory以及其子类如ApplicationContext提供了丰富的功能来支持不同的需求。ApplicationContextBeanFactory的子接口,提供了更多高级特性,如事件发布、国际化支持等。

核心接口解析

BeanFactory提供了几个核心方法,如getBean(String name),用于根据bean的名称获取一个bean实例。此外,containsBean(String name)检查容器中是否包含指定名称的bean。

Bean生命周期管理

Bean的生命周期由创建、初始化、使用到销毁几个阶段组成。BeanFactory负责整个生命周期的管理。其中,初始化和销毁回调是两个重要的生命周期事件。

Bean定义的解析过程

Spring容器启动时,会解析配置文件或注解,构建每个bean定义的BeanDefinition对象。这个过程涉及到读取配置元数据并转换成容器内部结构。

Bean的实例化与依赖注入

Bean的实例化主要有两种方式:构造函数实例化和静态工厂方法实例化。依赖注入(DI)是Spring实现IoC的手段之一,主要有构造器注入和setter方法注入。

初始化与销毁回调

Spring允许在bean的生命周期的特定点执行自定义逻辑,比如通过实现InitializingBeanDisposableBean接口。

案例分析:BeanFactory如何管理Bean

让我们通过一个简单的例子来看看BeanFactory是如何管理Bean的。考虑一个简单的UserService类,我们将展示如何通过XML配置来声明这个Bean,并通过BeanFactory来获取和使用这个Bean。

<bean id="userService" class="com.example.UserService"/>
public class UserService {public void getUser() {System.out.println("Fetching user...");}
}ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.getUser();

预期输出:

Fetching user...

这个例子展示了BeanFactory如何通过配置文件解析、实例化Bean,并进行依赖注入。

Spring源码解析

让我们深入Spring的DefaultListableBeanFactory类,看看Spring是如何实现这些原理的。DefaultListableBeanFactoryBeanFactory接口的一个核心实现类,它管理着容器中的bean定义(BeanDefinition)和bean实例。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {// 存储bean定义的映射private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// 实例化bean的核心逻辑}
}

让我们更深入地探讨DefaultListableBeanFactory类中的createBean方法,以及Spring是如何处理bean的实例化、依赖注入和初始化的。这将帮助我们更好地理解Spring框架的内部工作原理。

实例化

DefaultListableBeanFactory中,实例化bean的过程主要是通过其父类AbstractAutowireCapableBeanFactorycreateBeanInstance方法完成的。这个方法首先会尝试使用构造函数来创建bean实例,如果找到合适的构造函数,它会通过反射来实例化对象。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {// 确定构造函数并实例化bean
}

在这个过程中,Spring会考虑到构造函数的参数匹配,确保能够找到与配置相匹配的最适合的构造函数进行实例化。

依赖注入

一旦bean被实例化,Spring接下来会进行依赖注入。这一步是通过populateBean方法完成的。在这个方法中,Spring会检查bean定义中的依赖关系,并通过反射来设置bean的属性值或者通过方法注入依赖的对象。

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {// 获取bean的属性值并注入
}

这里,Spring处理了两种类型的依赖注入:基于属性的注入和基于构造函数的注入。对于基于属性的注入,Spring会解析属性值(可能是另一个bean的引用或简单类型的值)并通过setter方法或直接字段访问来注入这些值。对于基于构造函数的注入,这一步骤实际上已经在实例化阶段处理完毕。

初始化

最后,一旦bean被成功实例化并注入所需的依赖,Spring会调用initializeBean方法来处理bean的初始化。这个方法会按顺序执行BeanPostProcessor前置处理、调用自定义的初始化方法(比如实现InitializingBean接口或通过init-method指定的方法),以及BeanPostProcessor后置处理。

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {// 执行BeanPostProcessors的前置处理// 调用bean的初始化方法// 执行BeanPostProcessors的后置处理
}

在这个阶段,Spring提供了强大的扩展点,允许开发者通过实现BeanPostProcessor接口来在bean的初始化前后执行自定义逻辑。

通过以上解析,我们可以看到DefaultListableBeanFactory类中的createBean方法涵盖了bean生命周期的关键阶段:实例化、依赖注入和初始化。每个阶段都有其详细的处理逻辑,保证了bean的正确创建和配置。

测试代码

为了验证我们的理解,我们可以编写一个简单的测试用例来模拟BeanFactory的使用场景。

public class BeanFactoryTest {@Testpublic void testBeanLifecycle() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userService", UserService.class);userService.getUser();}
}

预期结果应该与前面的示例相同,显示"Fetching user...",证明我们通过BeanFactory成功地管理了UserService的生命周期。

第三章:ApplicationContext —— 超越BeanFactory

在Spring的世界里,ApplicationContext是一个闪耀的星辰,它不仅继承了BeanFactory的能力,更在此基础上提供了更加丰富和高级的功能。让我们一步步揭开ApplicationContext的神秘面纱,探索它超越BeanFactory的奥秘。

ApplicationContext与BeanFactory的关系

首先,我们需要明确ApplicationContextBeanFactory之间的关系。简单来说,ApplicationContextBeanFactory的子接口,它提供了更加全面的功能。如果说BeanFactory是基础的Spring容器,那么ApplicationContext就是提供了额外增强功能的高级容器。🌱 ➡️ 🌳

ApplicationContext的核心功能

ApplicationContext的核心功能可以说是非常强大,包括但不限于:

  • 国际化消息处理:支持不同语言环境的消息访问和管理。
  • 事件发布与监听机制:允许应用内部各个组件之间进行松耦合的通信。
  • 环境抽象与配置文件管理:提供了一种抽象的方式来处理配置信息,支持不同环境下的配置变更。
国际化消息处理

ApplicationContext中,国际化是通过MessageSource接口来实现的。这使得我们能够很方便地在不同语言环境下获取消息。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
String message = context.getMessage("hello.message", null, Locale.US);
System.out.println(message);

假设我们有一个messages_en_US.properties文件,其中包含以下内容:

hello.message=Hello, World!

上述代码将会输出:

Hello, World!
事件发布与监听机制

ApplicationContext提供了一个基于观察者模式的事件发布与监听机制。这使得应用内部的组件可以发布和监听事件,从而实现松耦合的交互。

public class MyEvent extends ApplicationEvent {public MyEvent(Object source) {super(source);}
}public class MyListener implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("Received event: " + event);}
}ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
context.publishEvent(new MyEvent("Some event"));

这段代码定义了一个自定义事件MyEvent和一个事件监听器MyListener。当事件被发布时,监听器会捕获到这个事件并执行相应的处理逻辑。

环境抽象与配置文件管理

ApplicationContext通过Environment接口提供了一种强大的环境抽象机制,允许我们灵活地管理和访问配置信息。

Environment env = context.getEnvironment();
String property = env.getProperty("some.property");
System.out.println(property);

这使得我们可以根据当前的运行环境(开发、测试、生产等)动态地调整配置信息。

实现原理揭秘

深入到ApplicationContext的实现原理,我们会发现它是通过一系列复杂但精妙的设计模式来实现上述功能的。以事件发布与监听机制为例,其背后是观察者模式的运用。

public class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {private final ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();// 其他实现细节...
}

AbstractApplicationContext中持有一个ApplicationEventMulticaster,负责管理事件的发布和监听。当事件发布时,ApplicationEventMulticaster会通知所有注册的监听器。

如何实现Bean的自动装配

ApplicationContext通过@Autowired注解和@ComponentScan注解等提供了强大的自动装配能力。这背后是Spring的依赖注入(DI)机制。

@Component
public class MyComponent {@Autowiredprivate MyDependency dependency;// 类体...
}

Spring容器会自动寻找类型匹配的bean,并注入到MyComponentdependency字段中。

AOP支持的背后机制

最后,ApplicationContext对AOP(面向切面编程)的支持也是其核心功能之一。Spring AOP是通过代理模式实现的,它允许开发者在不修改源码的情况下增强方法的行为。

@Aspect
public class MyAspect {@Before("execution(* com.example.MyComponent.myMethod(..))")public void beforeAdvice(JoinPoint joinPoint) {System.out.println("Before method: " + joinPoint.getSignature().getName());}
}

在这个例子中,MyAspect定义了一个前置通知,它会在MyComponentmyMethod方法执行前被调用。

本文未完待续....

下篇传送门点这里       

下章预告:

第四章:深挖ApplicationContext的高级特性

  • 环境与配置文件的灵活管理
    • Profile的工作原理
    • 使用PropertySources优雅地管理配置
  • 数据访问与事务管理的抽象
    • 数据访问异常的统一处理
    • 声明式事务管理的实现机制

第五章:Spring表达式语言(SpEL)

  • SpEL的设计目的与应用场景
  • SpEL的核心语法与功能
  • 如何通过SpEL实现动态配置

这篇关于【Spring源码解读!底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/694921

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr