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

news2024/9/25 15:22:48

 🎉🎉欢迎光临🎉🎉

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

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

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

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

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


 

目录

深挖ApplicationContext的高级特性

环境与配置文件的灵活管理

Profile的工作原理

使用PropertySources优雅地管理配置

数据访问与事务管理的抽象

数据访问异常的统一处理

声明式事务管理的实现机制

实践:验证声明式事务的工作原理

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

SpEL的设计目的与应用场景

SpEL的核心语法与功能

如何通过SpEL实现动态配置

总结:BeanFactory与ApplicationContext的精髓

如何继续深入学习Spring


深挖ApplicationContext的高级特性

在Spring框架中,ApplicationContext 被誉为Spring的心脏,负责管理Bean的生命周期和提供配置框架的各种高级特性。本篇博客将深入探讨ApplicationContext的几个高级特性,包括环境与配置文件的灵活管理、Profile的工作原理、使用PropertySources管理配置的优雅方式,以及数据访问与事务管理的抽象和实现机制。我们不仅会通过源码解读这些特性背后的设计思想,还会提供代码示例来验证我们的观点。

环境与配置文件的灵活管理

Spring允许开发者通过多种方式灵活管理应用的配置,包括但不限于属性文件、YAML文件、环境变量和命令行参数。这一切得益于EnvironmentPropertySources抽象。

Profile的工作原理

Profile允许开发者为不同的环境(如开发、测试、生产)定义不同的配置。通过激活特定的Profile,可以加载相应环境的配置。

@Configuration
@Profile("dev")
public class DevConfig {
    // 配置类内容
}

上述代码展示了如何定义一个仅在开发环境下激活的配置类。Spring根据当前激活的Profile来决定是否加载该配置类。

使用PropertySources优雅地管理配置

PropertySources是Spring环境抽象的一部分,它允许开发者从多个来源灵活地加载配置

Environment env = applicationContext.getEnvironment();
String property = env.getProperty("some.property");

通过Environment接口,可以方便地访问配置属性。

数据访问与事务管理的抽象

Spring提供了一套与具体技术无关的数据访问异常层次结构,使得异常处理更加统一和简便。

数据访问异常的统一处理

Spring将底层数据访问技术(如JDBC、Hibernate等)抛出的异常转换为DataAccessException体系中的异常,从而避免了与特定技术的耦合。

声明式事务管理的实现机制

Spring的声明式事务管理依赖于AOP(面向切面编程),允许开发者通过声明的方式来管理事务,而无需编写传统的事务管理代码。

@Transactional
public void someTransactionalMethod() {
    // 方法体
}

上述@Transactional注解表明该方法应在事务的上下文中运行。Spring在幕后自动创建和管理事务。

实践:验证声明式事务的工作原理

为了进一步理解声明式事务的工作原理,我们可以编写一个简单的测试用例来验证事务的行为。

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TransactionConfig.class})
public class TransactionalTest {

    @Autowired
    private TestService testService;

    @Test(expected = DataAccessException.class)
    public void testTransactionalMethod() {
        testService.someTransactionalMethod();
    }
}

在这个测试中,someTransactionalMethod方法在遇到数据访问异常时能够回滚事务。

如果事务正确回滚,那么这个测试应该通过,因为我们期望遇到DataAccessException

为了演示和验证事务的回滚机制,我们可以通过一个简单的Spring Boot应用中的服务层方法来模拟。假设我们有一个UserService,它负责处理用户的注册逻辑。在用户注册的过程中,我们故意引入一个数据访问异常,以触发事务回滚。

首先,我们定义一个简单的用户实体User和对应的数据访问接口UserRepository(这里只是为了演示,不涉及具体的数据库操作代码):

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    
    // 省略构造函数、Getter和Setter方法
}

public interface UserRepository extends JpaRepository<User, Long> {
    // 这里可以添加一些自定义的数据访问方法
}

接下来,我们实现UserService,并在其中添加一个注册用户的方法,该方法会故意抛出一个DataAccessException来模拟数据访问异常:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void registerUser(User user) {
        // 正常的用户注册逻辑
        userRepository.save(user);
        System.out.println("用户注册成功");

        // 故意抛出数据访问异常来模拟异常情况
        if (user.getUsername().equals("triggerException")) {
            throw new DataAccessException("模拟数据访问异常") {};
        }
    }
}

最后,我们编写一个测试用例来验证当registerUser方法遇到数据访问异常时,事务是否能够正确回滚:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    @Test(expected = DataAccessException.class)
    public void testRegisterUserWithException() {
        User user = new User();
        user.setUsername("triggerException");

        try {
            userService.registerUser(user);
        } finally {
            // 验证用户没有被保存到数据库,即事务回滚了
            Optional<User> foundUser = userRepository.findByUsername("triggerException");
            assertFalse(foundUser.isPresent());
            System.out.println("事务回滚,用户未被保存到数据库");
        }
    }
}

我们首先尝试注册一个用户名为triggerException的用户,这将触发我们在registerUser方法中故意设置的数据访问异常。根据@Transactional注解的工作原理,一旦方法抛出异常,所有的数据变更都应该被回滚。

最后在捕获异常后,我们检查数据库是否存在该用户记录,找不到的,这样就证明事务确实被回滚了

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

在本章中,我们将探讨Spring表达式语言(SpEL)的设计目的、应用场景以及它的核心语法与功能。SpEL是一个强大的表达式语言,它可以在Spring框架中被广泛地应用于动态配置和表达式求值的场景。

SpEL的设计目的与应用场景

首先,让我们来了解一下SpEL的设计目的和适用场景。SpEL的主要设计目的是为了提供一个灵活而强大的表达式语言,使得Spring框架能够更好地支持动态配置和运行时求值的需求。

对于应用场景来说,SpEL可以被广泛地应用于以下方面:

  • 动态配置:SpEL可以通过表达式来动态地配置Spring中的bean属性、方法参数等,从而实现更加灵活的配置方式。

  • 运行时求值:SpEL可以在运行时对表达式进行求值,从而实现动态计算、判断和决策等功能。

SpEL的核心语法与功能

现在让我们深入研究一下SpEL的核心语法和功能。SpEL的语法结构类似于Java,但也引入了一些新的概念和符号,使得表达式更加灵活和强大。

  • 字面量表达式:SpEL支持各种类型的字面量,包括字符串、数字、布尔值等。例如,'Hello, SpEL!'表示一个字符串字面量。

  • 属性访问:使用.操作符可以访问对象的属性。例如,person.name表示访问person对象的name属性。

  • 方法调用:使用.或者[]操作符可以调用对象的方法。例如,person.getName()表示调用person对象的getName()方法。

  • 运算符:SpEL支持各种运算符,包括算术运算符、关系运算符、逻辑运算符等。例如,1 + 2表示加法运算。

  • 条件表达式:SpEL支持使用三元运算符?:进行条件判断。例如,age >= 18 ? '成年人' : '未成年人'表示根据age的值判断是否成年。

  • 集合操作:SpEL支持对集合进行操作,包括访问集合元素、过滤、投影等。例如,numbers.![#this * 2]表示将numbers集合中的每个元素乘以2。

如何通过SpEL实现动态配置

现在让我们看一个具体的示例,来说明如何通过SpEL实现动态配置。假设我们有一个简单的Java类Person,它有一个名为age的属性。

public class Person {
    private int age;
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
}

我们可以通过SpEL来动态地配置Person对象的age属性。在Spring的配置文件中,使用#{}包裹SpEL表达式。

<bean id="person" class="com.example.Person">
    <property name="age" value="#{ 18 + 2 }" />
</bean>

上述配置中,SpEL表达式18 + 2会在运行时求值,并将结果赋值给Person对象的age属性。假设我们创建了一个person对象并获取其年龄:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person.getAge());

输出结果为:20

通过这个示例,我们可以看到如何使用SpEL来实现动态配置,通过表达式来计算属性的值,使得配置更加灵活和可变。

总结:BeanFactory与ApplicationContext的精髓

BeanFactoryApplicationContext是Spring容器的核心。BeanFactory提供了高级IoC的配置机制,而ApplicationContext在此基础上添加了更多企业所需的功能,如事件发布、国际化消息支持等。AOP正是ApplicationContext提供的众多高级特性之一,通过它,我们能够以简洁的方式实现应用中的横切关注点。

如何继续深入学习Spring

深入学习Spring的最佳方式是通过实践。建议读者不仅要阅读官方文档,还应该关注Spring的新特性和最佳实践。同时,参与开源项目、阅读源码、编写自己的Spring应用,都是提升自己技术水平的有效途径。

希望这篇博客能够帮助你更好地理解Spring中的AOP特性,以及ApplicationContext的强大功能。记住,学习之路是永无止境的,让我们一起在Spring的世界里不断探索,不断前进。

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

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

相关文章

代码随想录算法训练营29期|day43 任务以及具体任务

第九章 动态规划 part05 1049. 最后一块石头的重量 II class Solution {public int lastStoneWeightII(int[] stones) {int sum 0;for (int i : stones) {sum i;}int target sum >> 1;//初始化dp数组int[] dp new int[target 1];for (int i 0; i < stones.lengt…

HttpServletResponse接口用于表示状态代码的字段

1. HttpServletResponse接口用于表示状态代码的字段 您已学习了状态代码以及可用于从servlet向客户机发送状态代码的HttpServletResponse接口的字段。下表列出了HttpServletResponse接口表示状态代码的一些其他字段。 字段状态代码描述SC_HTTP_VERSION_NOT_SUPPORTED505服务器…

TCP相关知识点

TCP相关知识点 参考&#xff1a; 《计算机网络》 (建议收藏)TCP协议灵魂之问&#xff0c;巩固你的网路底层基础 关于 TCP 三次握手和四次挥手&#xff0c;满分回答在此 (值得看) TCP处于网络体系结构中的运输层。 运输层主要为应用进程提供端到端的逻辑通信&#xff0c;然后对…

牛客网SQL进阶137:第二快/慢用时之差大于试卷时长一半的试卷

官网链接&#xff1a; 第二快慢用时之差大于试卷时长一半的试卷_牛客题霸_牛客网现有试卷信息表examination_info&#xff08;exam_id试卷ID, tag试卷类别,。题目来自【牛客题霸】https://www.nowcoder.com/practice/b1e2864271c14b63b0df9fc08b559166?tpId240 0 问题描述 试…

Java实现网上药店系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 药品类型模块2.3 药品档案模块2.4 药品订单模块2.5 药品收藏模块2.6 药品资讯模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 药品表3.2.3 药品订单表3.2.4 药品收藏表3.2.5 药品留言表…

codemirror光标样式问题

输入框为空时会显示placeholder&#xff0c;文字有点长换行了&#xff0c;光标竟然变成上图那样了&#xff0c;我试过如果placeholder文字是三行&#xff0c;那么光标就是三行高。真是见了鬼了。查到最后&#xff0c;原因竟然是因为我在用vue-codemirror时为了去掉一些basicSet…

JavaScript中call、apply、bind方法的应用与区别

在JavaScript中&#xff0c;call、apply和bind是函数的三个重要方法&#xff0c;它们虽然功能不同&#xff0c;但都可以用来改变函数的执行上下文或者传递参数。本文将分别介绍call、apply和bind方法的应用和区别&#xff0c;并附带示例代码。 一、call方法 call方法的作用是…

FX110网:高盛因监管不力被罚51.2万美元

近日&#xff0c;高盛因其监控流程和系统存在漏洞&#xff0c;被美国金融业监管局(FINRA)处以512500美元的罚款。 FINRA调查发现&#xff0c;2009年2月至2023年4月中旬&#xff0c;高盛未能将认证权证、股权、单位信托和部分场外交易股票证券纳入9份用于检测该公司及其客户是否…

vue 实现 手机号中间4位分格输入框(暂无选中标识

vue 实现 手机号中间4位分格输入框 效果图 <!--4位分格输入框--> <!--<template><div><div style"display: flex;"><div class"phone-input"><inputv-for"(digit, index) in digits":key"index"…

vue项目文件夹介绍

目录 Vue项目目录结构 项目介绍: node_modules 文件及子目录 src目录 assets 文件夹 components 文件夹 实例:简单的注册并使用组件 Vue项目目录结构 项目介绍: node_modules 文件及子目录 这个文件夹里面全部都是node的一些基础的依赖包&#xff0c;当我们拓展的安…

【Python4Delphi】学习笔记(一):介绍篇

一、前言&#xff1a; 1. python语言简介&#xff1a; 众所周知&#xff0c;python是目前非常流行的编程语言之一&#xff0c;自20世纪90年代初Python语言诞生至今&#xff0c;它已被逐渐广泛应用于系统管理任务的处理和Web编程。 由于Python语言的简洁性、易读性以及可扩展性…

大规模块存储 EC 系统构建

本文整理自 2023 年 7 月 DataFunSummit 2023 数据基础架构峰会——大规模存储架构分论坛的同名主题分享。 非常欢迎大家的到来&#xff0c;今天由我来分享百度智能云块存储 EC 系统的构建。块存储系统在百度智能云的产品名叫 CDS&#xff0c;底层 EC 系统由 Aries 承担。 今天…

04-Java建造者模式 ( Builder Pattern )

建造者模式 摘要实现范例 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象 一个Builder 类会一步一步构造最终的对象&#xff0c;该 Builder 类是独立于其他对象的 建造者模式属于创建型模式&#xff0c;它提供了一种创建对…

[当人工智能遇上安全] 11.威胁情报实体识别 (2)基于BiGRU-CRF的中文实体识别万字详解

您或许知道&#xff0c;作者后续分享网络安全的文章会越来越少。但如果您想学习人工智能和安全结合的应用&#xff0c;您就有福利了&#xff0c;作者将重新打造一个《当人工智能遇上安全》系列博客&#xff0c;详细介绍人工智能与安全相关的论文、实践&#xff0c;并分享各种案…

Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 ✨

目录 什么是 Spring&#xff1f;为什么它如此流行&#xff1f; IoC 容器&#xff1a;从“依赖倒置”到“控制反转” Bean&#xff1a;IoC 容器中的基本组件 Spring 中的配置方式&#xff1a;XML、注解和 JavaConfig Bean 的作用域和生命周期管理 Bean 的属性装配和自动装配…

[大厂实践] Netflix容器平台内核panic可观察性实践

在某些情况下&#xff0c;K8S节点和Pod会因为出错自动消失&#xff0c;很难追溯原因&#xff0c;其中一种情况就是发生了内核panic。本文介绍了Netflix容器平台针对内核panic所做的可观测性增强&#xff0c;使得发生内核panic的时候&#xff0c;能够导出信息&#xff0c;帮助排…

Java ieda 抽风报错导致无法正常启动项目

Java ieda 抽风报错导致无法正常启动项目 问题描述&#xff1a;新建模块运行时出现下面报错&#xff0c;不能正常启动程序。 Error:Module 你的项目名 production: java.lang.ClassCastException: class org.jetbrains.jps.builders.java.dependencyView.TypeRepr$PrimitiveT…

适用于 Windows 11/10/8.1/8/7 的最佳 SD 卡恢复软件

丢失了 SD 卡中的一些重要照片或文档&#xff0c;并且不知道如何恢复&#xff1f;好吧&#xff0c;别担心&#xff01;&#xff01;以下是一些适用于 Windows 的最佳 SD 卡恢复工具&#xff0c;可增加您检索意外删除、丢失或丢失数据的机会。 什么是 SD 卡恢复软件&#xff1f;…

华为配置访客接入WLAN网络示例(MAC优先的Portal认证)

配置访客接入WLAN网络示例&#xff08;MAC优先的Portal认证&#xff09; 组网图形 图1 配置WLAN MAC优先的Portal认证示例组网图 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件 业务需求 某企业为了提高WLAN网络的安全性&#xff0c;采用MAC优先的外置Portal认…

JVM-运行时数据区程序计数器

运行时数据区 Java虚拟机在运行Java程序过程中管理的内存区域&#xff0c;称之为运行时数据区。《Java虚拟机规范》中规定了每一部分的作用。 程序计数器的定义 程序计数器&#xff08;Program Counter Register&#xff09;也叫PC寄存器&#xff0c;每个线程会通过程序计数器…