第一讲-BeanFactory和ApplicationContext
第一讲 BeanFactory和ApplicationContext
0. 本系列教程采用的是JDK8版本,SpringBoot 2.5.5版本
1. 容器接口
- BeanFactory能做哪些事情?
- ApplicationContext有哪些扩展功能
- 事件解耦
我们首先看一个简单一点的SpringBoot程序
package com.cherry.chapter1.a01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class A01Application {
public static void main(String[] args) {
SpringApplication.run(A01Application.class, args);
}
}
run方法会有一个返回值,返回的结果就是我们的Spring容器:
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
我们看一下ConfigurableApplicationContext
的类图(ctrl+Alt+U)
我们发现,ApplicationContext 间接继承了 BeanFactory 接口, 而 ConfigurableApplicationContext 继承了 ApplicationContext 接口, 因此 ConfigurableApplicationContext 也是间接继承了 BeanFactory 的接口。
到底什么才是 BeanFactory呢?
- 它是 ApplicationContext 的父接口
- BeanFactory 才是 Spring IOC 的核心容器,主要是 ApplicationContext 组合了 BeanFactory 的功能
比如说从IOC容器中获取 Bean 对象,最终还是用BeanFactory中获取!换句话说,我们可以从 ApplicationContext 中获取 BeanFactory 属性,进而从 BeanFactory 中管理 Bean 对象,比如我在下面的代码中打上断点,查看 context 当中的内容:
2. BeanFactory的功能
BeanFactory 能做哪些事情?我们首先看一下 BeanFactory 的定义,看一下该方法的所有方法(ctrl+f12):
比如查看 bean 是否存在,获取 bean 的别名,获取 bean 对象等等。表面上看上去只有 bean 的一些基本操作,实际上 BeanFactory 的实现类实现了 Bean 的生命周期管理,控制反转,依赖注入等等。
BeanFactory 其中一个的实现类就是 DefaultListableBeanFactory, 我们可以看一下这个类的关系图:
接下来我们看一下Spring容器中的单例 Bean 对象:
package com.cherry.chapter1.a01;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.reflect.Field;
import java.util.Map;
@SpringBootApplication
public class A01Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// System.out.println(context);
// 获取DefaultSingletonBeanRegistry中存储单例bean的属性
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
// 由于该属性是私有的,因此要设置允许访问该私有变量
singletonObjects.setAccessible(true);
// 获取 beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map= (Map<String, Object>)singletonObjects.get(beanFactory);
// 遍历
map.forEach((k,v)->{
System.out.println(k+"===>"+v);
});
}
}
查看打印结果:(当然了,SpringBoot中单例 bean 非常的多,这里只打印一部分!)
defaultServletHandlerMapping===>null
applicationTaskExecutor===>org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor@160396db
characterEncodingFilter===>org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter@7adbd080
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration===>org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration@7a799159
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration===>org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration@40ab8a8
preserveErrorControllerTargetClassPostProcessor===>org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor@6ff37443
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration===>org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration@65cc8228
org.springframework.context.annotation.internalConfigurationAnnotationProcessor===>org.springframework.context.annotation.ConfigurationClassPostProcessor@53093491
freeMarkerConfiguration===>freemarker.template.Configuration@21719a0
......
现在,我们手动在IOC容器中创建两个Bean:
@Component
public class Component2 {
}
@Component
public class Component1 {
}
对这两个bean进行过滤
package com.cherry.chapter1.a01;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.reflect.Field;
import java.util.Map;
@SpringBootApplication
public class A01Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// System.out.println(context);
// 获取DefaultSingletonBeanRegistry中存储单例bean的属性
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
// 由于该属性是私有的,因此要设置允许访问该私有变量
singletonObjects.setAccessible(true);
// 获取 beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map= (Map<String, Object>)singletonObjects.get(beanFactory);
// // 遍历
// map.forEach((k,v)->{
// System.out.println(k+"===>"+v);
// });
map.entrySet().stream().filter(e->e.getKey().startsWith("component"))
.forEach(e->{
System.out.println(e.getKey()+"===>"+e.getValue());
});
}
}
测试如下:
component1===>com.cherry.chapter1.a01.Component1@6732726
component2===>com.cherry.chapter1.a01.Component2@474821de
3. ApplicationContext的功能
ApplicationContext相比如BeanFactory多了哪些功能?我们再次查看一下之前的类结构继承图:
我们发现发现ApplicationContext在继承了BeanFactory的基础上,还实现了如下四个功能:
- MessageSource:处理国际资源化的能力
- ApplicationEventPublisher:发布事件的能力
- ResourcePatternResolver:处理路径资源的能力
- EnvironmentCapable:处理环境信息的能力
接下来依次介绍上面的四种能力:
3.1 处理国际化资源的能力
在resources文件夹下建立application.properties文件
在resources文件夹下建立messages.properties文件,用于存储通用的翻译结果
在resources文件夹下建立messages_en.properties文件,用于存储英文的翻译结果
hi=hello
在resources文件夹下建立messages_ja.properties文件,用于存储日文的翻译结果
hi=こんにちは
在resources文件夹下建立messages_zh.properties文件,用于存储中文的翻译结果
hi=你好
在主程序中获取各个语言的翻译:
System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
结果如下:
你好
こんにちは
hello
3.2 处理路径资源的能力
例如寻找classpath下的配置文件,如下的代码:
Resource[] resources = context.getResources("classpath:application.properties");
for (Resource r: resources){
System.out.println(r);
}
查看运行结果:
class path resource [application.properties]
3.5 处理环境信息的能力
该功能可以从环境中读取一些配置信息,包括系统缓环境变量,配置文件等中的信息
System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));
System.out.println(context.getEnvironment().getProperty("server.port"));
运行结果如下:
D:\development_tools\jdk17\jdk-17.0.10
8080
3.4 发布事件的能力
首先定义一个事件:
package com.cherry.chapter1.a01;
import org.springframework.context.ApplicationEvent;
// 用户注册事件
public class UserRegisterEvent extends ApplicationEvent {
// source: 事件源
public UserRegisterEvent(Object source) {
super(source);
}
}
发布事件:
context.publishEvent(new UserRegisterEvent(context));
编写一个监听器,在Spring中任何一个组件都可以作为监听器,监听器用于接收事件:
package com.cherry.chapter1.a01;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class Component2 {
@EventListener
public void aaa(UserRegisterEvent event){
System.out.println(event);
}
}
运行结果如下:
com.cherry.chapter1.a01.UserRegisterEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@66ac5762, started on Fri Jul 19 21:06:07 CST 2024]
发布事件这个功能的最大功能就是对功能进行解耦合,例如我有这样一个需求,用户注册方式可以有多种,可以是手机号,可以是邮箱等等,为了注册这个功能的扩展性,我们就可以使用发布事件这个功能,例如:
首先编写一个事件发布器:
package com.cherry.chapter1.a01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class Component1 {
// 事件发布器
@Autowired
private ApplicationContext context;
public void register(){
System.out.println("用户注册");
context.publishEvent(new UserRegisterEvent(this));
}
}
编写一个处理的事件类
package com.cherry.chapter1.a01;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class Component2 {
@EventListener
public void aaa(UserRegisterEvent event){
System.out.println(event);
System.out.println("发送短信");
}
}
从容器中获取事件发布的bean对象并调用refister()方法发布事件
context.getBean(Component1.class).register();
运行结果如下:
用户注册
com.cherry.chapter1.a01.UserRegisterEvent[source=com.cherry.chapter1.a01.Component1@6f231ced]
发送短信
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构