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

 🎉🎉欢迎光临🎉🎉

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

🌟特别推荐给大家我的最新专栏《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 {@Autowiredprivate 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 {@Autowiredprivate UserRepository userRepository;@Transactionalpublic 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 {@Autowiredprivate UserService userService;@Autowiredprivate 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.rhkb.cn/news/253646.html

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

相关文章

「深度学习」循环神经网络RNN

一、序列模型的例子 二、数学符号定义 X^{(i)<t>}&#xff1a;训练样本 i 的输入序列的第 t 个元素。 T_{X}^{i}&#xff1a;训练样本 i 的输入序列的长度。 Y^{(i)<t>}&#xff1a;训练样本 i 的输出序列的第 t 个元素。 T_{Y}^{i}&#xff1a;训练样本 i 的输…

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

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

Linux(二)

远程登录 Linux大多应用于服务器&#xff0c;而服务器不可能像PC一样放在办公室&#xff0c;它们是放在IDC机房的&#xff0c;所以我平时登录linux系统都是通过远程登录的。 Linux系统中是通过ssh服务实现的远程登录功能。默认ssh服务开启了22端口&#xff0c;而且当我们安装…

计划任务功能优化,应用商店上架软件超过100款,1Panel开源面板v1.9.6发布

2024年2月7日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.9.6版本。 在v1.9.5和v1.9.6这两个小版本中&#xff0c;1Panel针对计划任务等功能进行了多项优化和Bug修复。此外&#xff0c;1Panel应用商店新增了3款应用&#xff0c;上架精选软件应用超过1…

基于大规模连续多目标优化的共轭梯度-进化集成算法

声明&#xff1a;文章题目字数有限&#xff0c;翻译水平有限&#xff0c;仅供参考&#xff01; 原题目: Integrating Conjugate Gradients Into Evolutionary Algorithms for Large-Scale Continuous Multi-Objective Optimization 引&#xff1a;这么久了&#xff0c;又捡起…

C++之std::tuple(一) : 使用精讲(全)

相关系列文章 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)&#xff1a;深入剖析 深入理解可变参数(va_list、std::initializer_list和可变参数模版) std::apply源码分析 目录 1.简介 2.std::ignore介绍 3.创建元组 3.1.…

spring boot打完jar包后使用命令行启动,提示xxx.jar 中没有主清单属性

在对springBoot接口中间件开发完毕后&#xff0c;本地启动没有任何问题&#xff0c;在使用package命令打包也没异常&#xff0c;打完包后使用命令行&#xff1a;java -jar xxx.jar启动发现报异常&#xff1a;xxx.jar 中没有主清单属性&#xff0c;具体解决方法如下&#xff1a;…

华为云账号注销之后账号下的域名丢了怎么办?记录一次域名转移权限的经历

背景 我之前在阿里云上买了个域名&#xff0c;有效期10年的&#xff0c;然后在2023年1月末转移到华为云了&#xff0c;交了一年的域名费用&#xff0c;买了一个一年的华为云服务器 一年之后&#xff0c;华为云的服务器也到期了&#xff0c;我就想着参加新用户计划&#xff0c…

vscode 无法远程连接waiting the server log

使用版本 报错信息 相关日志 [17:32:59.765] > Waiting for server log... [17:32:59.801] > Waiting for server log... [17:32:59.831] > > * > * Visual Studio Code Server > * > * By using the software, you agree to > * the Visual Studio…

[算法前沿]--059-大语言模型Fine-tuning踩坑经验之谈

前言 由于 ChatGPT 和 GPT4 兴起,如何让人人都用上这种大模型,是目前 AI 领域最活跃的事情。当下开源的 LLM(Large language model)非常多,可谓是百模大战。面对诸多开源本地模型,根据自己的需求,选择适合自己的基座模型和参数量很重要。选择完后需要对训练数据进行预处…

MySQL篇----第十四篇

系列文章目录 文章目录 系列文章目录前言一、MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?二、锁的优化策略三、索引的底层实现原理和优化四、什么情况下设置了索引但无法使用前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽…

BGP协议

1.BGP相关概念 1.1 BGP的起源 不同自治系统&#xff08;路由域&#xff09;间路由交换与管理的需求推动了EGP的发展&#xff0c;但是EGP的算法简单&#xff0c;无法选路&#xff0c;从而被BGP取代。 自治系统&#xff1a;&#xff08;AS&#xff09; IGP&#xff1a;自治系统…

【Linux】gdb调试与make/makefile工具

目录 导读 1. make/Makefile 1.1 引入 1.2 概念 1.3 语法规则 1.4 示例 2. Linux调试器-gdb 2.1 引入 2.2 概念 2.3 使用 导读 我们在上次讲了Linux编辑器gcc\g的使用&#xff0c;今天我们就来进一步的学习如何调试&#xff0c;以及makefile这个强大的工具。 1. mak…

OpenCV-32 膨胀操作

膨胀是与腐蚀相反的操作&#xff0c;基本原理是只要保证卷积核的锚点是非0值&#xff0c;周边无论是0还是非0值&#xff0c;都变为0。 使用API---dilate&#xff08;img&#xff0c; kernel&#xff0c; iterationms 1&#xff09; 示例代码如下&#xff1a; import cv2 imp…

【图论】基环树

基环树其实并不是树&#xff0c;是指有n个点n条边的图&#xff0c;我们知道n个点n-1条边的连通图是树&#xff0c;再加一条边就会形成一个环&#xff0c;所以基环树中一定有一个环&#xff0c;长下面这样&#xff1a; 由基环树可以引申出基环内向树和基环外向树 基环内向树如…

学习VR全景拍摄,如何选择适合的VR全景设备?

随着VR全景技术的不断成熟和发展&#xff0c;VR全景已经成为摄影爱好者、地产行业、中介经纪人、广告、企业宣传等行业从业者们乐于尝试的新领域、新手段。 如何选择合适的VR全景设备成为了一个重要的问题。今天&#xff0c;和大家聊一聊&#xff0c;不同行业、人群和用途更适合…

【Qt】Android上运行keeps stopping, Desktop上正常

文章目录 问题 & 背景背景问题 解决方案One More ThingTake Away 问题 & 背景 背景 在文章【Qt】最详细教程&#xff0c;如何从零配置Qt Android安卓环境中&#xff0c;我们在Qt中配置了安卓开发环境&#xff0c;并且能够正常运行。 但笔者在成功配置并完成上述文章…

【蓝桥杯冲冲冲】[NOIP2017 提高组] 宝藏

蓝桥杯备赛 | 洛谷做题打卡day29 文章目录 蓝桥杯备赛 | 洛谷做题打卡day29[NOIP2017 提高组] 宝藏题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2提示题解代码我的一些话[NOIP2017 提高组] 宝藏 题目背景 NOIP2017 D2T2 题目描…

ChatGPT辅助编程,一次有益的尝试

如果大家想学习PCIe&#xff0c;搜索网上的信息&#xff0c;大概率会看到chinaaet上Felix的PCIe扫盲系列的博文 Felix-PCIe扫盲 每次看这个系列博文的时候&#xff0c;我都在想有没有什么方法可以把这个系列的博文都保存到一个pdf文件中&#xff0c;这样方便阅读。于是有了下…

tkinter绘制组件(41)——菜单按钮

tkinter绘制组件&#xff08;41&#xff09;——菜单按钮 引言布局函数结构按钮部分菜单显示完整代码函数 效果测试代码最终效果 github项目pip下载结语 引言 TinUI5的新控件&#xff0c;菜单按钮&#xff0c;menubutton。 这是一个与TinUI菜单&#xff08;menubar&#xff0…