Spring中的容器接口

devtools/2024/9/24 21:44:57/

容器接口

首先了解一下`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 的。

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

java">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 来查看效果。

java">@Component
public class Component1 {
}
java">@Component
public class Component2 {
}

进行过滤

java">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 进行翻译

java">        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

java">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);
}
java">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这两个环境变量(这里不用区分大小写)

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

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

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

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

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

java">context.publishEvent(new UserRegisteredEvent(context));
java">[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]

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

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

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

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

我们进行模拟一下:

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

java">@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));}
}

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

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

进行调用一下

java">context.getBean(Component1.class).register();

结果:

java">[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.ppmy.cn/devtools/116679.html

相关文章

德蒂企鹅PAEDIPROTECT:德国医研力作,专为敏感肌婴幼儿量身打造

新生儿的诞生总是伴随着喜悦&#xff0c;也充满着手忙脚乱&#xff0c;尤其是敏感肌宝宝的皮肤护理。宝宝的皮肤如同初绽的花瓣&#xff0c;皮肤角质层薄而脆弱&#xff0c;容易受到外界刺激物的影响&#xff0c;水分流失快&#xff0c;经常会出现干燥、瘙痒、红斑甚至湿疹等症…

胤娲科技:DeepMind的FermiNet——带你穿越“薛定谔的早餐桌”

当AI遇上量子迷雾&#xff0c;FermiNet成了你的“量子导航仪” 想象一下&#xff0c;你早晨醒来&#xff0c;发现家里的厨房变成了薛定谔的实验室&#xff0c;你的咖啡杯和吐司同时处于“存在与不存在”的叠加态。 你伸手去拿&#xff0c;却不确定会不会摸到冰冷的空气或是热腾…

关于中断和异常的一些理解

异常向量表的理解&#xff0c;每个异常都有对应的异常号码即中断号&#xff0c;根据发生的异常号去异常向量表(数组)里面执行对应的异常服务函数。这段话的表述哪里有问题&#xff1f; 总体上是正确的&#xff0c;但可以进一步澄清和细化几个方面&#xff0c;以增强对异常向量…

电脑如何设置代理IP:详细步骤指南

在网络世界中&#xff0c;代理IP是一种非常实用的工具。它不仅能保护你的隐私&#xff0c;还能访问更多的网络资源。今天&#xff0c;我们就来详细讲解一下如何在电脑上设置代理IP&#xff0c;让你轻松掌握这项技能。 什么是代理IP&#xff1f; 代理IP&#xff0c;简单来说&am…

element-plus表格操作

elememt-plus安装见上文 表格的特性 element-plus中的表格和原版表格最大的不同是写法不同&#xff0c;原版表格以行的方式写&#xff0c;element-plus以列的方式写。 element-plus的表格可以更方便的展示数据&#xff0c;只需要考虑数据的格式即可。 表格标签 表格标签有两种…

深圳mes制造系统的主要功能

深圳MES系统的主要功能包括以下几个方面&#xff1a; 生产计划管理&#xff1a;MES系统可以帮助企业制定生产计划&#xff0c;包括订单管理、生产排程、生产任务分配等&#xff0c;实现生产资源的合理配置和生产计划的优化。 生产过程控制&#xff1a;MES系统可以监控生产过程中…

2024年模式识别与图像分析国际学术会议(PRIA 2024)

2024年模式识别与图像分析国际学术会议&#xff08;PRIA 2024&#xff09; 2024 International Conference on Pattern Recognition and Image Analysis 2024年10月18-20日 南京 三轮截稿日期&#xff1a;10月10日 2024年模式识别与图像分析国际学术会议&#xff08;PRIA 2…

初识模版!!

初识模版 1.泛型编程1.1 如何实现一个交换函数呢&#xff08;使得所有数据都可以交换&#xff09;&#xff1f;1.2 那可以不可以让编译器根据不同的类型利用该模子来生成代码呢&#xff1f; 2.模版类型2.1 模版概念2.2 函数模版的原理2.3 函数模板的实例化2.4 模板参数的匹配原…