第一讲-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 对象等等。表面上看上去只有 getBean 的一些基本操作,实际上 BeanFactory 的实现类实现了 Bean 的生命周期管理,控制反转,依赖注入等等,不过这些功能都是由 BeanFactory 的实现类实现的,其中一个实现类是 DefaultListableBeanFactory, 我们可以看一下这个类的关系图:

DefaultListableBeanFactory 可以管理所有的 bean 对象,而在管理所有的 bean 对象中,其中大家最熟悉的就是管理单例对象,
DefaultListableBeanFactory 使用管理单例 bean 对象的功能,其实使用的就是该类的其中一个父类 DefaultSingletonBeanRegistry。
对于查看 Spring 容器中究竟有哪些单例,一种是通过 debug 的方式查看,还有一种是通过反射来获取,我们通过后者尝试获取下其中的单例 bean。
接下来我们看一下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]
我们再举个例子,寻找 spring boot 中的 spring.factories 文件看看(需要注意的是 classpath 只是在类路径下寻找,如果想要找的文件在 jar 包里使用 classpath 是找不到的,那如何在 jar 包里找到我们想要的文件呢?那就要在 classpath的后面加上一个星号****,即 classpath):
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
if (resources != null && resources.length > 0) {
for (Resource resource : resources) {
System.out.println(resource);
}
}
查看运行结果:
URL [jar:file:/opt/homebrew/Cellar/mvn_repository/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/opt/homebrew/Cellar/mvn_repository/org/springframework/boot/spring-boot-autoconfigure/2.5.5/spring-boot-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/opt/homebrew/Cellar/mvn_repository/org/springframework/spring-beans/5.3.10/spring-beans-5.3.10.jar!/META-INF/spring.factories]
URL [jar:file:/opt/homebrew/Cellar/mvn_repository/org/springframework/boot/spring-boot-test/2.5.5/spring-boot-test-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/opt/homebrew/Cellar/mvn_repository/org/springframework/boot/spring-boot-test-autoconfigure/2.5.5/spring-boot-test-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/opt/homebrew/Cellar/mvn_repository/org/springframework/spring-test/5.3.10/spring-test-5.3.10.jar!/META-INF/spring.factories]
3.3处理环境信息的能力
该功能可以从环境中读取一些配置信息,包括系统缓环境变量,配置文件等中的信息,例如下面的例子:
System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));
System.out.println(context.getEnvironment().getProperty("server.port"));
运行结果如下:
/opt/homebrew/Cellar/openjdk@17/17.0.16
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]
发送短信

浙公网安备 33010602011771号