Spring中的容器接口

容器接口

首先了解一下`BeanFactory`和`ApplicationContext`这两个接口的关系。

其实在一个 SpringBoot 项目中,这个 SpringBoot 项目的启动类的返回值就是一个 ApplicationContext 接口的实现类。

然后在 IDEA 中选中这个类,按住ctrl+alt+U可以查看类图,观察 ApplicationContextBeanFactory之间的关系。

这里可以发现:

  1. BeanFactoryApplicationContext的父类
  2. ApplicationContext接口不仅仅继承了BeanFactory接口还继承了其他的接口,所以说ApplicationContext不仅组合BeanFactory的功能,还有其他的功能。

接下来就简单的介绍一下这两个接口

BeanFactory

介绍:
简单说`BeanFactory`就是:
  1. 它是ApplicationContext的父接口
  2. 它是Spring的核心容器,主要的ApplicationContext实现都【**组合/借助】**了它的功能。

所谓的组合/借助是什么意思?

我们可以通过ApplicationContextgetBean()方法进行查看源码:

我们可以发现ApplicationContext的 getBean 方法其实内部组合调用了BeanFactory的 getBean 方法。所以说其实这个方法并不是ApplicationContext接口提供的,而是间接调用BeanFactory接口来实现的(并没有完全重写 getBean 方法,而是内部组合的BeanFactory的方法)。

既然说了组合,而且从上面的源码也可以看出来,其实BeanFactoryApplicationContext内部的成员变量。

功能:
想要知道 `BeanFactory`可以干点什么我们可以查看源码:

通过查看源码,表面上我们看着这个结构只有getBean有用

但是实际上,控制反转,基本的依赖注入,直至 Bean 的生命周期的各个功能,都是由它的实现类实现的。

单例Bean管理
其中它的最主要的实现类是`DefaultListableBeanFactory`,可以简单的看一下类图。![](https://img-blog.csdnimg.cn/img_convert/e855329d444df3d4c87ea832ca011728.png)

我们都知道 Bean 一般都是单例的形式保存的,从类图中就可以发现有一个接口主要是用来实现保存**单例Bean的,**接下来我们就尝试获取到BeanFactory中的单例Bean。

首先我们进入到DefaultSingletonBeanRegistry的源码中。

我们可以发现有一个 HashMap,就是用来专门存储单例 Bean 的。

接下来可以通过反射来获取这个值并且遍历查看。

Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> stringObjectMap = (Map<String, Object>) singletonObjects.get(beanFactory);
stringObjectMap.forEach((k,v) -> {System.out.println(k + "====>" + v);
});

可以看到有非常多单例bean被管理

这里我们可以自己创建两个 Bean 来查看效果。

@Component
public class Component1 {
}
@Component
public class Component2 {
}

进行过滤

Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> stringObjectMap = (Map<String, Object>) singletonObjects.get(beanFactory);
stringObjectMap.entrySet().stream().filter(stringObjectEntry -> stringObjectEntry.getKey().startsWith("component"))
.forEach(e -> {System.out.println(e.getKey() + "=====>" + e.getValue());
});
component1=====>com.sahuid.a01.Component1@4642b71d
component2=====>com.sahuid.a01.Component2@1450078a

ApplicationContext

这里我们再观察一下 ApplicationContext 的类图。

这里可以发现,除了BeanFactory的功能以外,还实现了四个接口,这四个接口就相当于是ApplicationContext的拓展能供。

功能:
国际化翻译能力
这个具体指的是可以将文字翻译成多种语言的能力,具体的接口是`MessageSource`。

这里需要在resource目录下配置配置 message 文件

其中第一个是通用的文件,就是所有语言通用的翻译。

然后第二个是英文。

第三个是日文。

在里面配置相关的翻译

然后通过 ApplicationContext 进行翻译

        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));System.out.println(context.getMessage("hi", null, Locale.JAPANESE));

注意:

这里可能会出现乱码的情况,所以我们需要修改一个 properties 文件的编码方式。

获取资源对象
这里可以通过`context`对象的`getResource()`方法来获取路径下的单个资源信息。或者`getResources()`获取多个资源。

其中参数里有几种通配符:

classpath:从当前类路径下找文件

classpath*:查找包括jar包里面的文件

file:当前磁盘文件下的文件。

接下来我们就调用一下查到单个和多个文件的方法:

我们这里分别查到当前类下的properties文件,和一个和Spring boot 自动装配相关的文件路径为META-INF/spring.facotories

Resource[] resources = context.getResources("classpath:application.properties");
for (Resource resource : resources) {System.out.println(resource);
}
System.out.println("===== 多个文件");
Resource[] contextResources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource contextResource : contextResources) {System.out.println(contextResource);
}
class path resource [application.properties]
===== 多个文件
URL [jar:file:/C:/Users/Lenovo/.m2/repository/org/springframework/boot/spring-boot/2.7.6/spring-boot-2.7.6.jar!/META-INF/spring.factories]
URL [jar:file:/C:/Users/Lenovo/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.7.6/spring-boot-autoconfigure-2.7.6.jar!/META-INF/spring.factories]
URL [jar:file:/C:/Users/Lenovo/.m2/repository/org/springframework/spring-beans/5.3.24/spring-beans-5.3.24.jar!/META-INF/spring.factories]
获取环境变量
`ApplecationContext`还可以获取环境变量,这里面的环境变量包括系统的环境变量和`properties`中设置的环境变量。

下面我们就调用来查看一下java_homeserver.port这两个环境变量(这里不用区分大小写)

String java_home = context.getEnvironment().getProperty("java_home");
System.out.println(java_home);
String port = context.getEnvironment().getProperty("server.port");
System.out.println(port);
D:\Java\jdk8
9000
发布事件
其中`context`的`publishEvent`方法就是用来发布的事件的,不过我们在发布事件之前需要一个事件,我们创建一个`UserRegisteredEvent`类用来充当事件,它需要继承`ApplicationEvent`这个类,然后写一个构造方法,里面的参数就是事件的源头,由谁发送事件,就传入谁。
public class UserRegisteredEvent extends ApplicationEvent {public UserRegisteredEvent(Object source) {super(source);}
}

然后我们还需要一个事件的监听器,这个监听器可以是容器中的任何一个bean我们只需要添加一个EventListener注解,并在参数中写入需要监听什么样的事件。

这里我们就用Component2来充当监听器

@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);@EventListenerpublic void aaa(UserRegisteredEvent event){log.info("接受到的事件:{}", event);}
}

然后我们就发布事件,发布的源头就是我们的context

context.publishEvent(new UserRegisteredEvent(context));
[INFO ] 07:27:48.428 [main] com.sahuid.a01.Component2           - 接受到的事件:com.sahuid.a01.UserRegisteredEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1649b0e6, started on Sat Sep 14 07:27:46 CST 2024]

这样我们就发现了其实这个事件发布就有一个解耦的作用。

比如我们现在有一个注册的功能,注册之后需要发送短信,在不解耦的情况下,就是写在一起的。并且如果后面有很多种的话,我们就需要在这个方法里面添加很多种方法,并进行判断。

@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);public void register(){log.info("用户注册");log.info("发送短信");}
}

现在我们使用事件发布的话,我们只需要关注用户注册这一件事情就好,当注册完成之后发布一个事件让发送短信的监听器监听到,然后处理相关逻辑即可,这样注册功能里面不会绑定着其他功能的代码,进行了解耦。

我们进行模拟一下:

用户创建好了我们就创建发布一条消息

@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);@Autowiredprivate ApplicationEventPublisher context;public void register(){log.info("用户注册");context.publishEvent(new UserRegisteredEvent(this));}
}

然后我们监听器那边就可以监听到处理相关逻辑:

@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);@EventListenerpublic void aaa(UserRegisteredEvent event){log.info("接受到的事件:{}", event);log.info("发送短信");}
}

进行调用一下

context.getBean(Component1.class).register();

结果:

[INFO ] 07:46:40.872 [main] com.sahuid.a01.Component1           - 用户注册 
[INFO ] 07:46:40.872 [main] com.sahuid.a01.Component2           - 接受到的事件:com.sahuid.a01.UserRegisteredEvent[source=com.sahuid.a01.Component1@6dcd5639] 
[INFO ] 07:46:40.874 [main] com.sahuid.a01.Component2           - 发送短信 

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

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

相关文章

JavaScript 安装库npm报错

今天在编写JavaScript代码时&#xff0c;缺少了包express。 const express require(express); const app express();app.get(/, (req, res) > {res.send(Hello, world!); });app.listen(3000, () > {console.log(Server is running on port 3000); });npm install exp…

小程序开发设计-小程序的宿主环境:组件⑦

上一篇文章导航&#xff1a; 小程序开发设计-小程序的宿主环境&#xff1a;宿主环境简介⑥-CSDN博客https://blog.csdn.net/qq_60872637/article/details/142425131?spm1001.2014.3001.5501 注&#xff1a;不同版本选项有所不同&#xff0c;并无大碍。 1.小程序中组件的分类…

深度学习(1):基础概念与创建项目

文章目录 基础概念创建项目1.在Anaconda上创建序虚拟环境2.创建PyProject3.创建完成 基础概念 CPU&#xff08;中央处理器&#xff09; CPU 是计算机的核心部件&#xff0c;负责执行计算和逻辑操作。它按照指令序列进行任务处理&#xff0c;擅长处理串行任务。CPU 的性能直接…

【MyBatis 源码拆解系列】MyBatis 运行原理 - 读取 xml 配置文件

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;持续 分享大厂系统设计&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址&#xff1a;点击…

Nature:科研论文中正确使用ChatGPT的三个原则

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 美国科罗拉多大学安舒茨医学院的生物医学信息学研究员Milton Pividori&#xff0c;一直在探索如何将ChatGPT等AI工具该技术融入课题组日常科研任务&#xff0c;例如进行文献综…

远程升级不成功?背后“凶手”可能是模组差分包…

最近有客户反馈在乡村里频繁出现掉线的情况。通过换货、换SIM卡对比排查测试&#xff0c;发现只有去年某批采购的那批模块在客户环境附近会出现掉线的情况&#xff0c;而今年采购的模块批次就不会掉线&#xff0c;很奇怪。 这个出问题的模块&#xff0c;就是合宙4G-Cat.1低功耗…

01.前端面试题之ts:说说如何在Vue项目中应用TypeScript?

文章目录 一、前言二、使用Componentcomputed、data、methodspropswatchemit 三 、总结 一、前言 与link类似 在VUE项目中应用typescript&#xff0c;我们需要引入一个库vue-property-decorator&#xff0c; 其是基于vue-class-component库而来&#xff0c;这个库vue官方推出…

数据驱动农业——农业中的大数据革命

橙蜂智能公司致力于提供先进的人工智能和物联网解决方案&#xff0c;帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、埃域知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能&…

静态链接和动态链接的Golang二进制文件

关注TechLead&#xff0c;复旦博士&#xff0c;分享云服务领域全维度开发技术。拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;复旦机器人智能实验室成员&#xff0c;国家级大学生赛事评审专家&#xff0c;发表多篇SCI核心期刊学术论文&#xff0c;阿里云认…

抖音截流神器发布:不限量评论采集,实时推送,提升运营效率

在短视频风靡的今天&#xff0c;抖音成为品牌营销的新战场。如何在海量内容中脱颖而出&#xff0c;提升运营效率成为关键。本文将揭秘一款革命性的抖音运营工具&#xff0c;它不仅支持不限量评论采集&#xff0c;还实现了实时推送功能&#xff0c;助力运营者精准把握用户反馈&a…

保姆级 Stable Diffusion 教程,看完这篇就够了!

在美国科罗拉多州举办了一场新兴数字艺术家竞赛&#xff0c;一幅名为《太空歌剧院》的作品获得“数字艺术/数字修饰照片”类别的一等奖&#xff0c;神奇的是&#xff0c;该作品的作者并没有绘画基础&#xff0c;这幅画是他用 AI 生成的。 这让人们充分见识到AI 在绘画领域惊人的…

Shell实战(一)

Shell实战&#xff08;一&#xff09; 导语程序实例解压缩交互功能描述代码和运行结果实现解析 监视CPU和内存功能描述代码和运行结果实现解析 用户管理功能描述代码和运行结果实现解析 总结 导语 本篇引入三个书上的shell程序设计项目&#xff0c;由于书上的版本较老&#xf…

异构AI算力资源池:智能世界的新型基础设施

随着人工智能技术的飞速发展&#xff0c;AI应用对计算资源的需求日益增长。然而&#xff0c;传统的同构计算资源池无法满足AI应用对计算能力、能耗和成本的多样化需求。为此&#xff0c;异构AI算力资源池应运而生&#xff0c;成为未来智能世界的重要基础设施。 背 景 人工智能…

H3C交换机手动释放DHCP地址

原本的的配置&#xff0c;释放时间10天 导致所有的地址都被使用完 释放了地址池的地址 重新调整了超期时间为8小时

游戏行业数据集成“利器”

《黑神话&#xff1a;悟空》自公布以来&#xff0c;便在游戏界引起了巨大的轰动。这款游戏以其精湛的画面、精彩的剧情和深度的玩法&#xff0c;让无数玩家充满期待。而在其背后&#xff0c;游戏开发者们面临着诸多挑战&#xff0c;其中之一便是数据的集成与管理。竞争激烈的游…

【C++ Primer Plus习题】17.3

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: #include <iostream> #include <fstream> using namesp…

LeetCode Hot100 C++ 哈希 49.字母异位词分组

给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排…

2-99 基于matlab多尺度形态学提取眼前节组织

基于matlab多尺度形态学提取眼前节组织&#xff0c;通过应用不同尺度的结构元素进行边缘检测&#xff0c;再通过加权融合的思想来整合检测到的边缘&#xff0c;降低图像噪声的影响&#xff0c;提高边缘检测的精度。程序已调通&#xff0c;可直接运行。 下载源程序请点链接&…

线程的状态及join()插队方法

一、线程的状态 线程整个生命周期中有6种状态&#xff0c;分别为 NEW 新建状态 、RUNNABLE 可运行状态、TERMINATED 终止状态、TIMED_WAITING计时等待状态、WAITING 等待状态、BLOCKED 阻塞状态 线程各个状态之间的转换&#xff1a; 在 JAVA 程序中&#xff0c;一个线程对象通过…