点击查看【bilibili】
参考视频: 黑马程序员Spring视频教程,全面深度讲解spring5底层原理

红色字体类名称
蓝色字体超链接
黄色字体类型
绿色字体属性名称

1.容器接口

1
2
3
4
5
6
7
8
@SpringBootApplication
public class AdvancedSpringApplication {

public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(AdvancedSpringApplication.class, args);
}

}

在idea里查看ConfigurableApplicationContext的类图:ConfigurableApplicationContext的类图
ConfigurableApplicationContext 接口继承了 ApplicationContext 接口,而 ApplicationContext 接口又间接地继承了 BeanFactory 接口,除此之外还继承了其他很多接口,相当于对 BeanFactory 进行了拓展。

什么是 BeanFactory

BeanFactory 是SpringIoC容器 ,提供一系列Bean的接口。
基于它也有众多子接口:

  • 它是 ApplicationContext 的父接口
  • 它才是 Spring 的核心容器,主要的 ApplicationContext 实现 组合 了它的功能,也就是说,BeanFactory 是 ApplicationContext 中的一个成员变量。

常用的 context.getBean("xxx") 方法,其实是调用了 BeanFactory 的 getBean() 方法。

BeanFactory 能做什么?

image.png
通过这些方法定义可知,BeanFactory 表面上只有 getBean() 方法,但实际上 Spring 中的控制反转、基本的依赖注入、乃至 Bean 的生命周期中的各个功能都是由它的实现类提供。

image.png
DefaultListableBeanFactory 实现了 BeanFactory 接口,它能管理 Spring 中所有的 Bean,当然也包含 Spring 容器中的那些单例对象。

DefaultListableBeanFactory 还继承了 DefaultSingletonBeanRegistry 类,这个类就是用来管理 Spring 容器中的单例对象。

在 IDEA 提供的类图中选中 DefaultSingletonBeanRegistry,然后按下 F4 进入这个类。它有一个 Map 类型的成员变量 singletonObjects:

1
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

Map的key就是Bean的名称,value是此bean,即单例对象。

而我们要对单例bean操作的话, Spring提供SmartInitializingSingleton#afterSingletonsInstantiated()方法可以在单例Bean初始化后被调用处理。

现有如下两个Bean:

1
2
3
4
5
6
7
@Component
public class Component1 {
}

@Component
public class Component2 {
}

查看singletonObjects里是否存在这两个bean的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@SpringBootApplication
public class AdvancedSpringApplication {
public static void main(String[] args) {
// 启动Spring应用程序并返回应用程序上下文
ConfigurableApplicationContext run = SpringApplication.run(AdvancedSpringApplication.class, args);
Field singletonObjects = null;
// 尝试获取DefaultSingletonBeanRegistry类中名为"singletonObjects"的字段
// 该字段是Spring容器用来存储singleton对象的Map
try {
singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
// 设置该字段可访问,以便后续可以获取其值
singletonObjects.setAccessible(Boolean.TRUE);
// 获取Spring容器中singleton对象的Map
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(run.getBeanFactory());
// 过滤并打印所有以"component"开头的Bean的名称和值
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
.forEach(e -> System.out.println(e.getKey() + " " + e.getValue()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
1
2
component daidawang.advancedSpring.Component@1ee29c84
component1 daidawang.advancedSpring.Component1@7c8326a4

ApplicationContext 的功能

回顾ConfigurableApplicationContext类的类图:
ConfigurableApplicationContext的类图
ApplicationContext 除了继承 BeanFactory 外,还继承了:

  • MessageSource:使其具备处理国际化资源的能力
  • ResourcePatternResolver:使其具备使用通配符进行资源匹配的能力
  • EnvironmentCapable:使其具备读取 Spring 环境信息、配置文件信息的能力
  • ApplicationEventPublisher:使其具备发布事件的能力

MessageSource 的使用

主要用于提供国际化支持。
在 SpringBoot 项目的 resources 目录下创建 messages.properties、messages_en.properties、messages_zh_CN.properties、messages_zh_TW.properties 四个国际化文件,除 messages.properties 外,其余三个文件内容如下:

1
thanks=Thank you
1
thanks=谢谢
1
thanks=謝謝

注意检查 File > Settings > Editor > File Encodings 将确保 Project Encoding 、 Default Encoding 、 Properties files 设置为 UTF-8。
测试 MessageSource 接口中 getMessage() 方法的使用:

1
2
3
4
5
6
7
8
9
10
11
@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// --snip--

System.out.println(context.getMessage("thanks", null, Locale.ENGLISH));
System.out.println(context.getMessage("thanks", null, Locale.SIMPLIFIED_CHINESE));
System.out.println(context.getMessage("thanks", null, Locale.TRADITIONAL_CHINESE));
context.close();
}
1
2
3
Thank you
谢谢
謝謝

ResourcePatternResolver 的使用

主要用于解析带有通配符的资源路径,通常用于查找符合特定模式的所有资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// --snip--

Resource[] resources = context.getResources("classpath:application.properties");
Assert.isTrue(resources.length > 0, "加载类路径下的 application.properties 文件失败");

// 使用 classpath* 可以加载 jar 里类路径下的 resource
resources = context.getResources("classpath*:META-INF/spring.factories");
Assert.isTrue(resources.length > 0, "加载类路径下的 META-INF/spring.factories 文件失败");
context.close();
}

EnvironmentCapable 的使用

用于表示访问spring应用上下文中的 Environment 对象组件。
Environment对象提供访问外部配置的能力,例如系统属性、环境变量或配置文件读取属性值。
在application.yml 里添加如下配置:

1
2
3
4
5
6
#{"author.name":"daidawang"}
properties:
name: "daidawang"
age: 20
person:
gender: "man"

java_home 是从环境变量中读取,properties.name 则是从 application.yml 配置文件中读取。

1
2
3
4
5
6
7
8
9
10
@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// --snip--

System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("properties.name"));
context.close();
}
1
2
D:\software\work\jdk\jdk8\jdk
daidawang

ApplicationEventPublisher 的使用

点击进入 [48事件 - 监听器](AQWSSpring四十九讲杂项 (yuque.com) “超链接title”)

点击进入 [48事件 - 发布器](AQWSSpring四十九讲杂项 (yuque.com) “超链接title”)

ApplicationEventPublisher调用publishEvent() 方法传入ApplicationEvent 实例参数 来完成发布事件。
并通过@EventListener 注解来监听事件。
定义事件类UserRegisteredEvent:

1
2
3
4
5
6
public class UserRegisteredEvent extends ApplicationEvent {
private static final long serialVersionUID = 6319117283222183184L;
public UserRegisteredEvent(Object source) {
super(source);
}
}

将Component1 作为发送事件的 Bean:

1
2
3
4
5
6
7
8
9
10
@Slf4j
@Component
public class Component1 {
@Autowired
private ApplicationEventPublisher context;
public void register() {
log.info("用户注册");
context.publishEvent(new UserRegisteredEvent(this));
}
}

将Compinedt2作为事件监听器:

1
2
3
4
5
6
7
8
9
@Slf4j
@Component
public class Component2 {
@EventListener
public void aaa(UserRegisteredEvent event) {
log.info("{}", event);
log.info("发送短信");
}
}

在 main() 方法中使用 Component1 发送事件:

1
2
3
4
5
6
7
8
9
@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
// --snip--

context.getBean(Component1.class).register();
context.close();
}
1
2
3
2024-08-14T14:28:57.394+08:00  INFO 21560 --- [advanced-spring] [           main] d.advancedSpring.bean.Component1         : 用户注册
2024-08-14T14:28:57.394+08:00 INFO 21560 --- [advanced-spring] [ main] d.advancedSpring.bean.Component2 : daidawang.advancedSpring.bean.UserRegisteredEvent[source=daidawang.advancedSpring.bean.Component1@7fab4be7]
2024-08-14T14:28:57.395+08:00 INFO 21560 --- [advanced-spring] [ main] d.advancedSpring.bean.Component2 : 发送短信

2.容器实现

BeanFactory的实现

需要使用到BeanFactory的实现类DefaultListableBeanFactory。
有了Bean工厂还需要定义Bean,之后把定义的Bean注册到工厂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}

@Bean
public Bean2 bean2() {
return new Bean2();
}
}

@Slf4j
static class Bean1 {
public Bean1() {
log.debug("构造 Bean1()");
}

@Autowired
private Bean2 bean2;

public Bean2 getBean2() {
return bean2;
}
}

@Slf4j
static class Bean2 {
public Bean2() {
log.debug("构造 Bean2()");
}
}

自定义beanDefinition并注册到BeanFactory

bean工厂手动注册beanDefinition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class A02Application {
public static void main(String[] args) {
// 创建一个 DefaultListableBeanFactory 实例,用于管理 Bean 生命周期和依赖注入
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 使用 BeanDefinitionBuilder 创建并配置一个泛型 Bean 定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DdwangBeanFactory.Config.class)
//设置其作用域为 singleton
.setScope("singleton")
//获取 BeanDefinition 对象
.getBeanDefinition();
// 在 BeanFactory 中注册上述 BeanDefinition,注册名为 "config"
beanFactory.registerBeanDefinition("config", beanDefinition);
System.out.println("--------------------手动注册config开始打印-------------------------");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("--------------------手动注册config打印结束-------------------------");
}
}
1
2
3
--------------------手动注册config开始打印-------------------------
config
--------------------手动注册config打印结束-------------------------

说明现在Bean工厂 有且只有一个 名为config的Bean。
现在明明有注解 @Configuration 和 @Bean 为什么Bean工厂里没有呢?
是因为现在的 DefaultListableBeanFactory 缺少了解析注解的能力。

添加注解解析器

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
// --snip--

// 注册注解配置处理器,以便处理配置注解
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
System.out.println("---------------------注册注解配置处理器开始打印------------------------");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("---------------------注册注解配置处理器打印结束------------------------");
}
1
2
3
4
5
6
7
8
9
10
11
--------------------手动注册config开始打印-------------------------
config
--------------------手动注册config打印结束-------------------------
---------------------注册注解配置处理器开始打印------------------------
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
---------------------注册注解配置处理器打印结束------------------------

根据打印出的信息,可以看到有一个名为 org.springframework.context.annotation.internalConfigurationAnnotationProcessor 的 Bean,根据其所含的 ConfigurationAnnotationProcessor 字样,可以知道这个 Bean 就是用来处理 @Configuration 和 @Bean 注解的,将配置类中定义的 Bean 信息补充到 BeanFactory 中。

那为什么在 Bean 工厂中依旧没有 bean1 和 bean2 呢?

现在仅仅是将处理器添加到了 Bean 工厂,还没有使用处理器。

使用注解解析器

使用处理器很简单,先获取到处理器,然后再使用即可。像 internalConfigurationAnnotationProcessor 这样的 Bean,都有一个共同的类型,名为 BeanFactoryPostProcessor,因此可以:
添加并使用BeanFactoryPostProcessor后置处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
// --snip--

// 使用后置处理器
//根据类型获取工厂下的bean
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
// 获取所有 BeanFactoryPostProcessor实现的实例
.values()
//并调用它们的 postProcessBeanFactory 方法
.forEach(p -> p.postProcessBeanFactory(beanFactory));

System.out.println("---------------------添加并使用BeanFactory 后置处理器开始打印------------------------");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("---------------------添加并使用BeanFactory 后置处理器打印结束------------------------");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--------------------手动注册config开始打印-------------------------
config
--------------------手动注册config打印结束-------------------------
---------------------注册注解配置处理器开始打印------------------------
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
---------------------注册注解配置处理器打印结束------------------------
---------------------添加并使用BeanFactory 后置处理器开始打印------------------------
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
bean3
bean4
---------------------添加并使用BeanFactory 后置处理器打印结束------------------------

添加依赖注入功能

现在bean1 和 bean2 已经被添加到Bean工厂,
修改 bean1使之依赖于 bean2,查看是否成功注入到 bean1中:
bean1依赖于 bean2

1
2
3
4
5
6
7
8
9
10
11
12
13
@Slf4j
static class Bean1 {
public Bean1() {
log.debug("构造 Bean1()");
}

@Autowired
private Bean2 bean2;

public Bean2 getBean2() {
return bean2;
}
}
1
2
3
System.out.println("----------------------Bean后置处理器开始打印-----------------------");
System.out.println(beanFactory.getBean(DdwangBeanFactory.Bean1.class).getBean2());
System.out.println("----------------------Bean后置处理器打印结束-----------------------");
1
2
3
4
----------------------Bean后置处理器开始打印-----------------------
16:54:10.857 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1()
null
----------------------Bean后置处理器打印结束-----------------------

发现目前的bean工厂无法 注入 bean2 到bean1。
internalAutowiredAnnotationProcessor 后置处理器用于解析 @Autowired
internalCommonAnnotationProcessor 后置处理器用于解析 @Resource
它们都有一个共同的类型 BeanPostProcessor,因此可以:
添加并使用Bean后置处理器:BeanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
// --snip--
//添加并使用Bean后置处理器:BeanPostProcessor
//对 Bean 的生命周期的各个阶段提供拓展,例如 @AutoWired @Resource...
beanFactory.getBeansOfType(BeanPostProcessor.class)
.values()
.forEach(beanFactory::addBeanPostProcessor);
System.out.println("----------------------Bean后置处理器开始打印-----------------------");
System.out.println(beanFactory.getBean(DdwangBeanFactory.Bean1.class).getBean2());
System.out.println("----------------------Bean后置处理器打印结束-----------------------");
}
1
2
3
4
5
----------------------Bean后置处理器开始打印-----------------------
16:56:35.432 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1()
16:56:35.445 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2 -- 构造 Bean2()
daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2@587e5365
----------------------Bean后置处理器打印结束-----------------------

建立 BeanPostProcessor 和 BeanFactory 的关系后,bean2 被成功注入到 bean1 中了。

按需加载bean改为预加载

当需要使用 Bean 时,Bean 才会被创建,即按需加载。

那有没有什么办法预先就初始化好单例对象呢?

添加 beanFactory.preInstantiateSingletons();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class A02Application {
public static void main(String[] args) {
// 创建一个 DefaultListableBeanFactory 实例,用于管理 Bean 生命周期和依赖注入
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 使用 BeanDefinitionBuilder 创建并配置一个泛型 Bean 定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DdwangBeanFactory.Config.class)
//设置其作用域为 singleton
.setScope("singleton")
//获取 BeanDefinition 对象
.getBeanDefinition();

// 在 BeanFactory 中注册上述 BeanDefinition,注册名为 "config"
beanFactory.registerBeanDefinition("config", beanDefinition);
System.out.println("--------------------手动注册config开始打印-------------------------");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("--------------------手动注册config打印结束-------------------------");

// 注册注解配置处理器,以便处理配置注解
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
System.out.println("---------------------注册注解配置处理器开始打印------------------------");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("---------------------注册注解配置处理器打印结束------------------------");


//添加并使用BeanFactory 后置处理器 BeanFactoryPostProcessor 补充了一些 Bean 的定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
.values()
.forEach(p -> p.postProcessBeanFactory(beanFactory));
System.out.println("---------------------添加并使用BeanFactory 后置处理器开始打印------------------------");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("---------------------添加并使用BeanFactory 后置处理器打印结束------------------------");


//添加并使用Bean后置处理器:BeanPostProcessor 对 Bean 的生命周期的各个阶段提供拓展,例如 @AutoWired @Resource...
beanFactory.getBeansOfType(BeanPostProcessor.class)
.values()
.forEach(beanFactory::addBeanPostProcessor);

// 预先初始化单例对象(完成依赖注入和初始化流程)
beanFactory.preInstantiateSingletons();

System.out.println("----------------------Bean后置处理器开始打印,开启预先初始化单例-----------------------");
System.out.println(beanFactory.getBean(DdwangBeanFactory.Bean1.class).getBean2());
System.out.println("----------------------Bean后置处理器打印结束-----------------------");
}
}

添加beanFactory.preInstantiateSingletons();后控制台打印:

1
2
3
4
5
6
7
8
---------------------添加并使用BeanFactory 后置处理器打印结束------------------------
17:03:25.096 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1()
17:03:25.109 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2 -- 构造 Bean2()
17:03:25.110 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean3 -- 构造 Bean3()
17:03:25.111 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean4 -- 构造 Bean4()
----------------------Bean后置处理器开始打印,开启预先初始化单例-----------------------
daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2@4961f6af
----------------------Bean后置处理器打印结束-----------------------

发现在输出———-Bean后置处理器开始打印,开启预先初始化单例——– 之前bean就初始化了,

如何注释掉40行 beanFactory.preInstantiateSingletons();,

发现在输出———-Bean后置处理器开始打印,开启预先初始化单例——– 之后bean被调用才初始化。

1
2
3
4
5
6
---------------------添加并使用BeanFactory 后置处理器打印结束------------------------
----------------------Bean后置处理器开始打印,开启预先初始化单例-----------------------
17:04:48.876 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1()
17:04:48.891 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2 -- 构造 Bean2()
daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2@587e5365
----------------------Bean后置处理器打印结束-----------------------

总结BeanFactory的实现

通过上面代码测试得到,BeanFactory 不会 :

  • 不会主动调用 BeanFactory 后置处理器;
  • 不会主动添加 Bean 后置处理器;
  • 不会主动初始化单例对象;
  • 解析 ${} 和 #{} (未写代码)详见 4.7 ${}内嵌值后置处理器

后置处理器的排序

@Autowired根据类型注入,如果同种类型的 Bean 有多个且没有指定变量名则报错。

@Resource 根据名称注入。

如果使用 @Autowired 的变量名称注入bean3, 又使用 @Resource(name = “bean4”) 注入bean4 呢?

新增如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Configuration
static class Config {
// --snip--

@Bean
public Bean3 bean3() {
return new Bean3();
}

@Bean
public Bean4 bean4() {
return new Bean4();
}
}

interface Inter {

}


@Slf4j
static class Bean3 implements Inter {
public Bean3() {
log.info("构造 Bean3()");
}
}

@Slf4j
static class Bean4 implements Inter {
public Bean4() {
log.info("构造 Bean4()");
}
}


@Slf4j
static class Bean1 {
public Bean1() {
log.info("构造 Bean1()");
}

@Autowired
private Bean2 bean2;

public Bean2 getBean2() {
return bean2;
}

@Autowired
@Resource(name = "bean4")
private Inter bean3;

public Inter getInter() {
return bean3;
}
}
1
2
3
4
public static void main(String[] args) {
// --snip--
System.out.println(beanFactory.getBean(Bean1.class).getInter());
}
1
daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean4@707194ba

根据打印的结果可知,@Autowired 先生效了,这是因为 internalAutowiredAnnotationProcessor 排在 internalCommonAnnotationProcessor 之前。可以查看bean处理器的先后关系:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
// --snip--
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(i -> {
System.out.println(">>>> " + i);
beanFactory.addBeanPostProcessor(i);
});
}

1
2
>>>> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@385c9627
>>>> org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@139982de

通过流处理来改变它们的顺序:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
// --snip--
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(Objects.requireNonNull(beanFactory.getDependencyComparator()))
.forEach(i -> {
System.out.println(">>>> " + i);
beanFactory.addBeanPostProcessor(i);
});
System.out.println(beanFactory.getBean(Bean1.class).getInter());
}
1
2
3
>>>> org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@139982de
>>>> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@682b2fa
daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean4@445b295b

为什么使用 beanFactory.getDependencyComparator() 后就改变了 BeanPostProcessor 的先后顺序呢?

在调用的 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); 方法源码中有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {

DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
// 设置比较器
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}

// --snip--
}

进入AutowiredAnnotationBeanPostProcessor 源码:其oreder为 Integer.MAX_VALUE-2

进入CommonAnnotationBeanPostProcessor 源码:其oreder为 Integer.MAX_VALUE-3

值越小,优先级越大,就排在更前面。

所以CommonAnnotationBeanPostProcessor 的优先级高,@Resource 就先生效

ApplicationContext的实现

基础准备

1
2
3
4
5
6
7
8
9
static class Bean1 {

}

static class Bean2 {
@Getter
@Setter
private Bean1 bean1;
}

ClassPathXmlApplicationContext

读取类路径下的xml文件并初始化和管理bean.

基于 classpath 下的 xml 格式的配置文件来创建。

创建一个名为为b01的xml,并配置bean2

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bean1" class="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean1"/>

<bean id="bean2" class="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean2">
<property name="bean1" ref="bean1" />
</bean>
</beans>
1
2
3
4
5
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(context.getBean(Bean2.class).getBean1());
}
1
2
3
bean1
bean2
daidawang.advancedSpring.bean.a02.DdwangApplicationContext$Bean1@503d687a

FileSystemXmlApplicationContext

从磁盘路径下读取xml格式的配置文件

1
2
3
4
5
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\workspace\\advanced-spring\\src\\main\\resources\\b01.xml");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(context.getBean(Bean2.class).getBean1());
}
1
2
3
bean1
bean2
daidawang.advancedSpring.bean.a02.DdwangApplicationContext$Bean1@503d687a

XmlBeanDefinitionReader

ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 都依赖于从 XML 文件中读取 Bean 的信息,而这都利用了 XmlBeanDefinitionReader 进行读取。

1
2
3
4
5
6
7
8
9
10
private static void testXmlBeanDefinitionReader() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取前");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\b01.xml"));
System.out.println("读取后");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}

1
2
3
4
读取之前...
读取之后...
bean1
bean2

AnnotationConfigApplicationContext

基于Java配置类创建bean

先创建一个配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}

@Bean
public Bean2 bean2(Bean1 bean1) {
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
1
2
3
4
5
private static void testAnnotationConfigApplicationContext() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(context.getBean(Bean2.class).getBean1());
}
1
2
3
4
5
6
7
8
9
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
ddwangApplicationContext.Config
bean1
bean2
daidawang.advancedSpring.bean.a02.DdwangApplicationContext$Bean1@14555e0a

与基于XML创建的ApplicationContext相比,AnnotationConfigApplicationContext使得容器多了一些后置处理器相关的Bean。

如果要在先前的两种方式中也添加上这些 后置处理器Bean,可以在 XML 进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bean1" class="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean1"/>

<bean id="bean2" class="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean2">
<property name="bean1" ref="bean1" />
</bean>

<!-- 添加后置处理器 -->
<context:annotation-config />
</beans>

AnnotationConfigServletWebServerApplicationContext

基于Java配置类创建bean,内嵌WebServer用于Web环境的容器。

先定义配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
static class WebConfig {
//配置Tomcat服务器
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}

//核心控制器
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}

//注册控制器DispatcherServlet 到TomCat里
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}

/**
* 一个简单的控制器,用于处理根路径的GET请求,并返回"hello"消息
*/
@Bean("/hello")
public Controller controller() {
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
1
2
3
4
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}

运行代码,在浏览器中访问 http://localhost:8080/hello 路径则会显示出 hello 字样:

  • 内嵌的Tomcat是如何工作的(通过AnnotationConfigServletWebServerApplicationContex自动启动内嵌的 Tomcat 服务器,并将所有配置的 Bean 注册到上下文中)
  • web程序的入口都是通过 “/“ 的通配符,先由DispatcherServlet处理后分发请求到控制器
  • 一个最基础的web程序需要 Tomcat,DispatcherServlet,和将DispatcherServlet注册到Tomcat上

BeanDefinition

BeanDefinition 它封装了 Bean 基本信息,Spring 根据 BeanDefinition 就能创建出符合要求的 Bean。

读取 BeanDefinition 可以通过下列两种类完成:

  • BeanDefinitionReader
  • ClassPathBeanDefinitionScanner

BeanDefinitionReader

该接口中对 loadBeanDefinitions() 方法进行了多种重载,支持传入一个或多个 Resource 对象、资源位置来加载 BeanDefinition。

它有一系列相关实现,比如:

  • XmlBeanDefinitionReader:通过读取 XML 文件来加载;
  • PropertiesBeanDefinitionReader:通过读取 properties 文件来加载,此类已经被 @Deprecated 注解标记;

除此之外,还有一个 AnnotatedBeanDefinitionReader,尽管它并不是 BeanDefinition 的子类,但它们俩长得很像,根据其类注释可知:它能够通过编程的方式对 Bean 进行注册,是 ClassPathBeanDefinitionScanner 的替代方案,能读取通过注解定义的 Bean。

ClassPathBeanDefinitionScanner

通过扫描指定包路径下的 @Component 及其派生注解来注册 Bean,是 @ComponentScan 注解的底层实现。

BeanDefinitionRegistry

AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 中都有一个 BeanDefinitionRegistry 类型的成员变量,

它是一个接口,提供了 BeanDefinition 的增加、删除和查找功能。

注册与获取Bean

将 Bean 添加到 Spring 容器里的大致流程,

  • 利用 <font style="color:#DF2A3F;">BeanDefinitionReader</font> 读取配置文件或注解信息,为每一个 Bean 生成一个 <font style="color:#DF2A3F;">BeanDefinition</font>
  • 将 <font style="color:#DF2A3F;">BeanDefinition</font> 注册到 <font style="color:#DF2A3F;">BeanDefinitionRegistry</font> 中
  • 当创建 Bean 对象时,直接从 <font style="color:#DF2A3F;">BeanDefinitionRegistry</font> 中获取 <font style="color:#DF2A3F;">BeanDefinition</font> ,来生成 Bean。
  • 如果生成的 Bean 是单例的,Spring 会将它们保存到 <font style="color:#DF2A3F;">SingletonBeanRegistry</font> 中,即三级缓存的第一级缓存,后续需要时从这里面寻找,避免重复创建。

那么向 Spring 容器中添加单例 Bean 时,可以跳过注册 BeanDefinition,直接向 SingletonBeanRegistry 中添加创建完成的 Bean。既然添加的是创建完成的 Bean,所以 这个单例 Bean 不会经过 Spring 的生命周期。

SingletonBeanRegistry 是一个接口,它有一个子接口名为 ConfigurableListableBeanFactory,而这恰好是 BeanFactoryPostProcessor 接口中抽象方法的参数:

1
2
3
4
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

使用 BeanFactoryPostProcesser,注册创建完成的 Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Slf4j
public class TestBeanFactoryPostProcessor {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean("bean2", Bean2.class);
context.registerBean(MyBeanFactoryPostProcessor.class);
context.refresh();

Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(">>>>>>>>>>>>>>>>>>");
System.out.println(context.getBean(Bean1.class));
}

static class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Bean1 bean1 = new Bean1();
bean1.setName("mofan");
beanFactory.registerSingleton("bean1", bean1);
}
}

@Getter
@ToString
static class Bean1 {
@Setter
private String name;
private Bean2 bean2;

@Autowired
private void setBean2(Bean2 bean2) {
log.debug("依赖注入 bean2");
this.bean2 = bean2;
}

@PostConstruct
public void init() {
log.debug("初始化...");
}
}

static class Bean2 {
}
}
1
2
3
4
5
6
7
8
9
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean2
testBeanFactoryPostProcessor.MyBeanFactoryPostProcessor
>>>>>>>>>>>>>>>>>>
TestBeanFactoryPostProcessor.Bean1(name=mofan, bean2=null)

BeanDefinition 的名称数组中不包含 bean1,也没有输出任何与经过 Spring 生命周期相关的日志信息,容器中 bean1 里注入的 bean2 也是 null。这表明通过这种方式注册的 Bean 不会注册 BeanDefinition,也不会经过 Spring 生命周期。

根据前面的补充,现在可以AnnotatedBeanDefinitionReader这样注册并获取 Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DefaultListableBeanFactoryTest {
/**
* MyBean 类是一个空的测试 Bean 类,用于在测试中注册和检索
* 它没有任何实现,仅用作测试目的
*/
static class MyBean{

}
/**
* main 方法是测试程序的入口点
* 它展示了如何在 DefaultListableBeanFactory 中注册和检索 Bean
* @param args 命令行参数,本测试未使用
*/
public static void main(String[] args) {
// 创建一个 DefaultListableBeanFactory 实例
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 创建一个 AnnotatedBeanDefinitionReader 实例,用于向 BeanFactory 注册 Bean
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
// 注册 MyBean 到 BeanFactory
reader.registerBean(MyBean.class);

// 从 BeanFactory 中检索 MyBean 实例
MyBean bean = beanFactory.getBean(MyBean.class);
// 输出检索到的 Bean 实例
System.out.println(bean);
}
}

3. Bean生命周期

  1. 通过 Bean 后置处理器 的方式说明 Bean 生命周期:
  • @Autowired 依赖注入
  • @PostConstruct 初始化
  • @PreDestroy 销毁
  1. 通过 Bean 内置处理器 的方式说明 Bean 生命周期
  • InitializingBean 初始化
  • DisposableBean 销毁
  1. 配置文件中指定初始化和销毁方法 :
  • 标签。
  • <destroy-method=”destroyMethod”>标签。
  1. 使用@Bean注解指定初始化和销毁方法:
  • @Bean注解的initMethod属性来指定初始化
  • @Bean注解的destroyMethod属性来指定销毁方法。

自定义一个 SpringBoot 的主启动类:

1
2
3
4
5
6
7
8
@SpringBootApplication
public class A03Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args);
// 调用 close 方法,显示单例bean生命周期的销毁阶段
context.close();
}
}

在启动类所在路径下再定义一个类,使其能够被自动装配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Slf4j
@Component
public class LifeCycleBean {
// 构造
public LifeCycleBean() {
log.info("构造");
}
// 依赖注入
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home) {
log.info("依赖注入: {}", home);
}
// 初始化
@PostConstruct
public void init() {
log.info("初始化");
}
// 销毁
@PreDestroy
public void destroy() {
log.info("销毁");
}
}

运行主启动类,查看控制台的日志信息(只列举主要信息):

1
2
3
4
d.advancedSpring.bean.a03.LifeCycleBean  : 构造
d.advancedSpring.bean.a03.LifeCycleBean : 依赖注入: D:\software\work\jdk\jdk8\jdk
d.advancedSpring.bean.a03.LifeCycleBean : 初始化
d.advancedSpring.bean.a03.LifeCycleBean : 销毁

Spring 还提供了一些对 Bean 生命周期的各个阶段进行拓展的 BeanPostProcessor,比如 InstantiationAwareBeanPostProcessor 和 DestructionAwareBeanPostProcessor。

实现这两个接口,并使用 @Component 注解标记实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor , DestructionAwareBeanPostProcessor {

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if ("lifeCycleBean".equals(beanName)) {
log.info("<<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean");
}
// 返回 null,则不会替换原有的 bean
return null;
}

@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if ("lifeCycleBean".equals(beanName)) {
log.info("<<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点");
// return false;
}
return true;
}

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if ("lifeCycleBean".equals(beanName)) {
log.info("<<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource");
}
return pvs;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("lifeCycleBean".equals(beanName)) {
log.info("<<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties");
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("lifeCycleBean".equals(beanName)) {
log.info("<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强");
}
return bean;
}

@Override
public void postProcessBeforeDestruction(Object o, String beanName) throws BeansException {
if ("lifeCycleBean".equals(beanName)) {
log.info("<<<<<<<<<< 销毁执行之前,如 @PreDestroy");
}
}
}

再运行主启动类,查看控制台的日志信息(只列举主要信息):

1
2
3
4
5
6
7
8
9
10
11
12
d.a.bean.a03.MyBeanPostProcessor         : <<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean 
d.advancedSpring.bean.a03.LifeCycleBean : 构造
d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点
d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource
d.advancedSpring.bean.a03.LifeCycleBean : 依赖注入: D:\software\work\jdk\jdk8\jdk
d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties
d.advancedSpring.bean.a03.LifeCycleBean : 初始化
d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
d.a.bean.a03.A03Application : Started A03Application in 1.474 seconds (process running for 2.081)
d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 销毁执行之前,如 @PreDestroy
d.advancedSpring.bean.a03.LifeCycleBean : 销毁

什么是实例化 什么是初始化?

  • 实例化指调用构造方法
  • 初始化指调用init方法

模板方法设计模式

为什么实现了 BeanPostProcessor 接口后就能够在 Bean 生命周期的各个阶段进行拓展呢?

这就是模板方法设计模式。

模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。

使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

现有如下代码,模拟 BeanFactory 构造 Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory myBeanFactory = new MyBeanFactory();
myBeanFactory.getBean();
}

static class MyBeanFactory {
private static ArrayList<BeanPostProcessor> processors = new ArrayList<>();
/**
* getBean 方法是模板方法,它定义了创建 Bean 的算法流程。
* @return 创建好的 Bean 对象
*/
public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean);
System.out.println("初始化 " + bean);
return bean;
}
}
1
2
3
构造 java.lang.Object@49097b5d
依赖注入 java.lang.Object@49097b5d
初始化 java.lang.Object@49097b5d

现在需要在依赖注入后新增 解析功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* @version v1.0
* @ClassName: 模板方法
* @Description: 演示了模板方法设计模式的一个简单实现。
* * 模板方法模式定义了一个操作中的算法骨架,
* * 而将一些步骤延迟到子类中。
* * 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
*
* @Author: 呆大王
* @Date: 2024/8/15
*/
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory myBeanFactory = new MyBeanFactory();
myBeanFactory.addProcessor(bean -> System.out.println("解析 @Autowired"));
myBeanFactory.addProcessor(bean -> System.out.println("解析 @Resource"));
myBeanFactory.getBean();
}

/**
* MyBeanFactory 类作为模板方法模式中的抽象类角色,
* 定义了创建 Bean 的算法骨架,并允许子类提供具体的实现。
*/
static class MyBeanFactory {
private static ArrayList<BeanPostProcessor> processors = new ArrayList<>();
/**
* getBean 方法是模板方法,它定义了创建 Bean 的算法流程。
* 具体的处理逻辑(如依赖注入)被委托给 BeanPostProcessor 接口的实现。
* @return 创建好的 Bean 对象
*/
public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean);
for (BeanPostProcessor processor : processors) {
processor.inject(bean);
}
System.out.println("初始化 " + bean);
return bean;
}

/**
* addProcessor 方法允许向工厂中添加自定义的 BeanPostProcessor 实现。
* @param beanPostProcessor 需要添加的处理器
*/
public void addProcessor(BeanPostProcessor beanPostProcessor) {
processors.add(beanPostProcessor);
}
}

/**
* BeanPostProcessor 接口定义了 Bean 处理器的行为,
* 其中的 inject 方法由具体的实现类来完成。
*/
static interface BeanPostProcessor {
void inject(Object bean);
}
}
1
2
3
4
构造 java.lang.Object@5fd0d5ae
依赖注入 java.lang.Object@5fd0d5ae
解析 @Autowired
初始化 java.lang.Object@5fd0d5a

4. Bean后置处理器

编写如下三个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* @version v1.0
* @ClassName: Bean1
* @Description:
* @Author: 呆大王
* @Date: 2024/8/15
*/
@Slf4j
@ToString
public class Bean1 {
private Bean2 bean2;

@Autowired
public void setBean2(Bean2 bean2) {
log.info("@Autowired 生效: {}", bean2);
this.bean2 = bean2;
}

private Bean3 bean3;

@Resource
public void setBean3(Bean3 bean3) {
log.info("@Resource 生效: {}", bean3);
this.bean3 = bean3;
}

private String home;

/**
* @Autowired写在方法上,参数类型是UserService,那么Spring容器会自动注入UserService对象
* 参数是字符串类型 会注入失败,需要配合@Value注解
* @Value("${JAVA_HOME}") 注解用于从配置文件中读取JAVA_HOME属性的 值注入 到home变量中
*/

@Autowired
public void setHome(@Value("${JAVA_HOME}") String home) {
log.info("@Value 生效: {}", home);
this.home = home;
}

@PostConstruct
public void init() {
log.info("@PostConstruct 生效");
}

@PreDestroy
public void destroy() {
log.info("@PreDestroy 生效");
}
}
1
2
3
4
5
public class Bean2 {
}

public class Bean3 {
}

Bean2 和 Bean3 很简单,而在 Bean1 中使用了多个注解以实现 Bean 注入和值注入。

GenericApplicationContext容器

GenericApplicationContext 是一个干净的容器,而且GenericApplicationContext有refresh()方法初始化容器。

  • 而DefaultListableBeanFactory需要手动添加Bean处理器和BeanFactory处理器。

  • 而AnnotationConfigApplicationContext已经有Bean处理器,无法测试处理器具体作用。

    ​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class A04Application {
public static void main(String[] args) {
/**
* GenericApplicationContext 是一个干净的容器,
* 而且GenericApplicationContext有refresh方法初始化容器。
* 而DefaultListableBeanFactory需要手动添加Bean处理器和BeanFactory处理器。
* 而AnnotationConfigApplicationContext已经有Bean处理器,无法测试处理器具体作用。
*/
GenericApplicationContext context = new GenericApplicationContext();
// 用原始方式注册三个 bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);

// 初始化容器。执行 beanFactory 后置处理器,添加 bean 后置处理器,初始化所有单例
context.refresh();

// 销毁容器
context.close();
}
}

运行上述方法后,控制台中只打印了与 Spring 相关的日志信息,也就是说 Bean1 中使用的注解并没有生效。

@Autowired@Value后置处理器

向 GenericApplicationContext 添加一些与 Bean 后置处理器相关的 Bean,使得 Bean1 中使用的注解能够生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
// --snip--

context.registerBean("bean3", Bean3.class);

// 解析值注入内容
// 不写出现如下错误
// No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Value("${JAVA_HOME}")}
//这里的ContextAnnotationAutowireCandidateResolver引出第四十六讲 https://www.yuque.com/daidawang-jlmiu/tyvtug/hu3f16agn841fm9h
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
//注册解析@Autowired @Value的处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

context.refresh();

// --snip--
}
1
2
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@4386f16
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: D:\software\work\jdk\jdk8\jdk

@Resource@PostConstruct@PreDestroy处理器

@Autowired 和 @Value 注解成功生效,但 @Resource、@PostConstruct 和 @PreDestroy 依旧没有生效,因此还需要添加解析它们的 Bean 后置处理器。

1
2
3
4
5
6
7
8
public static void main(String[] args) {
// --snip--

//注册解析@Resource @PostConstruct @PreDestroy的处理器
context.registerBean(CommonAnnotationBeanPostProcessor.class);

// --snip--
}
1
2
3
4
5
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Resource 生效: daidawang.advancedSpring.bean.a04.Bean3@1b1473ab
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@3e92efc3
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: D:\software\work\jdk\jdk8\jdk
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @PostConstruct 生效: D:\software\work\jdk\jdk8\jdk
INFO daidawang.advancedSpring.bean.a04.Bean1 -- @PreDestroy 生效: D:\software\work\jdk\jdk8\jdk

看到上面控制台打印的朋友还能回答为什么 @Resource 比 @Autowired 先执行吗? 😏

@ConfigurationProperties后置处理器

编写带有@ConfigurationProperties 的Bean4,获取环境变量中 java.home 和 java.version 的信息。

1
2
3
4
5
6
@Data
@ConfigurationProperties(prefix = "java")
public class Bean4 {
private String home;
private String version;
}

对先前的 main() 方法进行补充:

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
// --snip--

context.registerBean("bean4", Bean4.class);

// --snip--

System.out.println(context.getBean(Bean4.class));

// --snip--
}
1
Bean4(home=null, version=null)

说明 Bean4 成功添加到容器中,但值注入失败了,显然也是因为缺少解析 @ConfigurationProperties 注解的后置处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
// --snip--

context.registerBean("bean4", Bean4.class);

// --snip--
//注册解析@ConfigurationProperties的处理器
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

System.out.println(context.getBean(Bean4.class));

// --snip--
}
1
Bean4(home=D:\software\work\jdk\jdk17, version=17.0.9)

后置处理器执行时机

在Bean的生命周期 里 @Autowired@Value的处理器 或者 @Resource@PostConstruct@PreDestroy处理器 @ConfigurationProperties的处理器 是什么时候执行的呢?

视频P17 的最后 016-第四讲-常见bean后处理器3_哔哩哔哩_bilibili 老师简单的提了一下但是没有写代码证明。

那么我们用已经学习到的知识去验证一下把!

如何不看下面的代码你通过 前面16讲的课程也可以独立写出来吗? 自己写写证明这些后置处理器的执行时机再来和我对一下把(我的也是自己写的不一定对)

  1. 将bean1加入容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @version v1.0
* @ClassName: Bean1
* @Description:
* @Author: 呆大王
* @Date: 2024/8/15
*/
@Slf4j
@ToString
@Component
public class Bean1 {
private Bean2 bean2;
// --snip--
}
  1. 创建 MyBeanPostProcessor 并实现 InstantiationAwareBeanPostProcessor , DestructionAwareBeanPostProcessor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Slf4j
@Component
class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if ("bean1".equals(beanName)) {
log.info("<<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean");
}
// 返回 null,则不会替换原有的 bean
return null;
}

@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if ("bean1".equals(beanName)) {
log.info("<<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点");
// return false;
}
return true;
}

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if ("bean1".equals(beanName)) {
log.info("<<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource");
}
return pvs;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("bean1".equals(beanName)) {
log.info("<<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties");
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("bean1".equals(beanName)) {
log.info("<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强");
}
return bean;
}

@Override
public void postProcessBeforeDestruction(Object o, String beanName) throws BeansException {
if ("bean1".equals(beanName)) {
log.info("<<<<<<<<<< 销毁执行之前,如 @PreDestroy");
}
}
  1. A04Application 上添加@SpringBootApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @version v1.0
* @ClassName: A04Application
* @Description:
* @Author: 呆大王
* @Date: 2024/8/15
*/
@SpringBootApplication
public class A04Application {
// --snip--
ConfigurableApplicationContext context1 = SpringApplication.run(A04Application.class, args);

context1.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2024-08-16T14:24:15.290+08:00  INFO 3052 --- [advanced-spring] [           main] d.a.bean.a04.MyBeanPostProcessor         : <<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean
2024-08-16T14:24:15.292+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点
2024-08-16T14:24:15.293+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource
2024-08-16T14:24:15.293+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @Resource 生效: daidawang.advancedSpring.bean.a04.Bean3@3a082ff4
2024-08-16T14:24:15.296+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@237f7970
2024-08-16T14:24:15.297+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @Value 生效: D:\software\work\jdk\jdk8\jdk
2024-08-16T14:24:15.298+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties
2024-08-16T14:24:15.298+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @PostConstruct 生效: D:\software\work\jdk\jdk8\jdk
2024-08-16T14:24:15.298+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强
2024-08-16T14:24:15.822+08:00 INFO 3052 --- [advanced-spring] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2024-08-16T14:24:15.830+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.A04Application : Started A04Application in 2.076 seconds (process running for 3.592)
2024-08-16T14:24:15.935+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 销毁执行之前,如 @PreDestroy
2024-08-16T14:24:15.935+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @PreDestroy 生效: D:\software\work\jdk\jdk8\jdk

AutowiredAnnotationBeanPostProcessor

通过上面的代码,知道 AutowiredAnnotationBeanPostProcessor 是处理解析 @Autowired @Value,

那么他的如何运行的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class AutowiredAnnotationBeanPostProcessor运行原理 {

@SneakyThrows
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//相比于使用BeanDefinitionBuilder创建BeanDefinition对象,
//直接将成品bean2,bean3注册到容器中,
//不会走创建bean的过程、依赖注入、初始化等操作
beanFactory.registerSingleton("bean2",new Bean2());
beanFactory.registerSingleton("bean3",new Bean3());

//解析@Value,ContextAnnotationAutowireCandidateResolver引入46讲@Value注入底层
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
//为了自动装配带有 @Autowired 的字段需要知道容器信息,所以需要设置
postProcessor.setBeanFactory(beanFactory);

Bean1 bean1 =new Bean1();
System.out.println(bean1);
// // 执行依赖注入,@Autowired、@Value
postProcessor.postProcessProperties(null,bean1,"bean1");
System.out.println(bean1);
}
}
1
2
3
4
Bean1(bean2=null, bean3=null, home=null)
15:41:49.646 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@3c0a50da
15:41:49.659 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: ${JAVA_HOME}
Bean1(bean2=daidawang.advancedSpring.bean.a04.Bean2@3c0a50da, bean3=null, home=${JAVA_HOME})

发现调用 AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法时,

Bean1 中的 bean2、成功且正常的被注入,

进入源码,我们发现 findAutowiringMetadata(),用于查找指定 bean对象哪些地方使用了 @Autowired、@Value 等与注入相关的注解,并将这些信息封装在 InjectionMetadata 对象中,之后调用其 inject() 方法利用反射完成注入。

查找带有@Autowired的方法

findAutowiringMetadata() 方法是一个私有方法,尝试利用反射进行调用并进行断点查看 InjectionMetadata 对象中的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SneakyThrows
public static void main(String[] args) {
// --snip--

AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
postProcessor.setBeanFactory(beanFactory);

Bean1 bean1 = new Bean1();
// // 执行依赖注入,@Autowired、@Value
// postProcessor.postProcessProperties(null, bean, "bean1");
Method method = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
method.setAccessible(true);
// 获取 Bean1 上加了 @Value、@Autowired 注解的成员变量、方法参数信息
InjectionMetadata metadata = (InjectionMetadata) method.invoke(postProcessor, "bean1", Bean1.class, null);
// 此处断点
System.out.println(bean1);
}

InjectionMetadata 中有一个名为 injectedElements 的集合类型成员变量,根据上图所示,injectedElements 存储了被相关注解标记的成员变量、方法的信息,因为 Bean1 中的 bean3 成员变量、setBean2() 和 setHome() 方法恰好被 @Autowired 注解标记。

反射依赖注入

然后按照源码一样,调用 InjectionMetadata#inject() 方法进行依赖注入:

1
2
3
4
5
6
7
8
9
10
11
@SneakyThrows
public static void main(String[] args) {
// --snip--

// 获取 Bean1 上加了 @Value、@Autowired 注解的成员变量、方法参数信息
InjectionMetadata metadata = (InjectionMetadata) method.invoke(postProcessor, "bean1", Bean1.class, null);

// 调用 InjectionMetadata 来进行依赖注入,注入时按类型查找值
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);
}
1
2
3
4
5
Bean1(bean2=null, bean3=null, home=null)
15:52:37.841 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@5a4aa2f2
15:52:37.853 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: ${JAVA_HOME}
Bean1(bean2=daidawang.advancedSpring.bean.a04.Bean2@5a4aa2f2, bean3=null, home=${JAVA_HOME})
org.springframework.beans.factory.annotation.InjectionMetadata@4de5031f

InjectionMetadata#inject() 是如何 根据不同的类型 都注入的呢

修改

Bean1,给private Bean3 bean3;上添加@Autowired 注解

1
2
+     @Autowired
private Bean3 bean3;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@SneakyThrows
public static void main(String[] args) {
// --snip--

// 如何按类型查找值
//成员变量bean3
Field bean3 = Bean1.class.getDeclaredField("bean3");
//封装成DependencyDescriptor对象
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
//去容器里找需要被注入的值
Object o1 = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o1);

//方法信息
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
// MethodParameter 构造方法的第二个参数表示需要解析的方法中参数的索引
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
Object o2 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o2);

Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object o3 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o3);
}

${}内嵌值后置处理器

上面的控制台打印,我们发现 home=${JAVA_HOME}),

还缺少${} 的后置处理器

1
2
3
4
5
6
7
8
@SneakyThrows
public static void main(String[] args) {
// --snip--

//解析${}的后置处理器
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);
// --snip--
}
1
2
3
4
5
Bean1(bean2=null, bean3=null, home=null)
Bean1(bean2=null, bean3=null, home=null)
16:11:57.283 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@5ea434c8
16:11:57.306 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: D:\software\work\jdk\jdk8\jdk
Bean1(bean2=daidawang.advancedSpring.bean.a04.Bean2@5ea434c8, bean3=null, home=D:\software\work\jdk\jdk8\jdk)

添加${} 的后置处理器后 home正常解析。

5. BeanFactory后置处理器

BeanFactory后置处理器为BeanFactory提供扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package daidawang.advancedSpring.bean.a05;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
* @version v1.0
* @ClassName: Config
* @Description:
* @Author: 呆大王
* @Date: 2024/8/16
*/

@Configuration
@ComponentScan()
public class Config {
@Bean
public Bean1 bean1()
{
return new Bean1();
}

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}

@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/advanced_spring");
dataSource.setName("root");
dataSource.setPassword("123456");
return dataSource;
}
}
1
2
3
4
5
6
7
8
9
10
package daidawang.advancedSpring.bean.a05;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Bean1 {
public Bean1() {
log.info("我被 Spring 管理啦");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package daidawang.advancedSpring.bean.a05.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class Bean2 {
public Bean2() {
log.info("我被 Spring 管理啦");
}
}

继续使用 GenericApplicationContext 作为容器,向容器中注册 config:

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);

context.refresh();

for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

context.close();
}
1
config

并没有打印出除 config 以外的 Bean 信息,也就是说 Config 类中的 @ComponentScan 和 @Bean 注解都没有生效。

根据经验,显然是因为缺少某个后置处理器,引出下文:

ConfigurationClassPostProcessor

ConfigurationClassPostProcessor 后置处理器: @ComponentScan @Bean @Import @ImportResource

1
2
3
4
5
6
7
8
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// @ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);

// --snip--
}
1
2
3
4
5
6
7
8
9
10
11
14:28:46.521 [main] INFO daidawang.advancedSpring.bean.a05.component.Bean2 -- 我被 Spring 管理啦
14:28:46.525 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦
14:28:46.660 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1,root} inited
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
bean2
bean1
sqlSessionFactoryBean
dataSource
14:28:46.716 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1} closing ...
14:28:46.717 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1} closed

MapperScannerConfigurer

在使用 MyBatis 时,经常会使用到 @Mapper 注解,而这个注解的解析也需要使用到特定的 BeanFactory 后置处理器。

以下两个接口被 @Mapper 注解标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package daidawang.advancedSpring.bean.a05.mapper;

import org.apache.ibatis.annotations.Mapper;


@Mapper
public interface Mapper1 {
}


package daidawang.advancedSpring.bean.a05.mapper;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface Mapper2 {
}

然后添加解析 @Mapper 注解的后置处理器 MapperScannerConfigurer :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package daidawang.advancedSpring.bean.a05;

import lombok.SneakyThrows;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

/**
* @version v1.0
* @ClassName: A05Application
* @Description:
* @Author: 呆大王
* @Date: 2024/8/16
*/
public class A05Application {
@SneakyThrows
public static void main(String[] args) {
//一个干净容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config",Config.class);
//@ComponentScan @Bean @Import @ImportResource
context.registerBean(ConfigurationClassPostProcessor.class);
//@MapperScanner
context.registerBean(MapperScannerConfigurer.class
// 设置扫描的基础包,以找到所有的Mapper接口
,bd->{bd.getPropertyValues().add("basePackage","daidawang.advancedSpring.bean.a05.mapper");});

context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
context.close();
}
}
1
2
mapper1
mapper2

添加了MapperScannerConfigurer 后置处理器后,mapper1 和mapper2 被注册到容器里

模拟实现

模拟实现@ComponentScan 组件扫描

模拟 beanFactory 后置处理器 ConfigurationClassPostProcessor ,是如何通过@ComponentScan 注解 将带有 @Component 及其派生注解 的类 注册到容器。

在bean2目录下创建bean3,bean4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package daidawang.advancedSpring.bean.a05.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;


@Slf4j
@Controller
public class Bean3 {
public Bean3() {
log.info("我被 Spring 管理啦");
}
}

package daidawang.advancedSpring.bean.a05.component;

import lombok.extern.slf4j.Slf4j;


@Slf4j
public class Bean4 {
public Bean4() {
log.info("我被 Spring 管理啦");
}
}

编写 ComponentScanPostProcessor 用于实现 @Component 注解的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package daidawang.advancedSpring.bean.a05;

import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;
import java.awt.*;
import java.io.IOException;

/**
* @version v1.0
* @ClassName: ComponentScanPostProcessor
* @Description: 自定义的ComponentScan 后置处理器,
* 用于在Spring应用上下文中自动注册被@Component注解标记的类为Bean
* @Author: 呆大王
* @Date: 2024/8/17
*/
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
@Override
@SneakyThrows
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
// 创建一个缓存元数据读取器工厂,用于读取类元数据
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// 创建一个命名策略生成器,用于生成Bean的名称
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
if (componentScan != null) {
for (String packageName : componentScan.basePackages()) {
System.out.println(packageName);
String path = "classpath*:" + packageName.replace(".", "/") + "/*.class";

for (Resource resource : new PathMatchingResourcePatternResolver().getResources(path)) {
MetadataReader reader = factory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// System.out.println("类名"+reader.getClassMetadata().getClassName());
// System.out.println("是否加了@Component "+ reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
// System.out.println("是否加了@Component的派生注解 "+ reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(
reader.getClassMetadata().getClassName())
.getBeanDefinition();

if (configurableListableBeanFactory instanceof DefaultListableBeanFactory beanFactory) {
String beanName = generator.generateBeanName(beanDefinition, beanFactory);
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ComponentScanPostProcessor.class);

context.refresh();

for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

context.close();
}
1
2
3
4
5
6
7
daidawang.advancedSpring.bean.a05.component
16:48:18.613 [main] INFO daidawang.advancedSpring.bean.a05.component.Bean2 -- 我被 Spring 管理啦
16:48:18.615 [main] INFO daidawang.advancedSpring.bean.a05.component.Bean3 -- 我被 Spring 管理啦
config
daidawang.advancedSpring.bean.a05.ComponentScanPostProcessor
bean2
bean3

总结

  1. 实现 BeanFactoryPostProcessor,并重写其方法
  2. 获取@ComponentScan 的值,并将值转为类路径
  3. 根据类路径获取路径下所有的Resource对象
  4. 根据Resource对象获取MetadataReader对象进而获取AnnotationMetadata对象
  5. AnnotationMetadata 判断 是否带有 @Component 或 其派生注解 的类
  6. 通过MetadataReader获取带有@Component 或 其派生注解 的类 的类路径 创建 BeanDefinition对象
  7. 通过AnnotationBeanNameGenerator 创建BeanName
  8. DefaultListableBeanFactory 注册 BeanDefinition 到容器

模拟实现注入@Bean

Config 类中再增加一个方法作为干扰项:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@ComponentScan("daidawang.advancedSpring.bean.a05.component")
public class Config {

public Bean2 bean2()
{
return new Bean2();
}

// --snip--
}

自行编写一个 BeanFactoryPostProcessor 的实现类用于解析 @Bean 注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package daidawang.advancedSpring.bean.a05;

import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.StringUtils;

/**
* @version v1.0
* @ClassName: DDWBeanPostProcessor
* @Description:
* @Author: 呆大王
* @Date: 2024/8/17
*/
public class DDWBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
@SneakyThrows
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//读取资源元数据创建一个CachingMetadataReaderFactory实例
//通过CachingMetadataReaderFactory读取资源比 反射 快
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("daidawang/advancedSpring/bean/a05/Config.class"));
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
for (MethodMetadata annotatedMethod : annotationMetadata.getAnnotatedMethods(Bean.class.getName())) {
// System.out.println(annotatedMethod.getMethodName());

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(annotatedMethod.getMethodName(), "config");
// 工厂方法、构造方法的注入模式使用构造器模式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

String initMethod = (String) annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("init");

if (StringUtils.hasLength(initMethod)) {
builder.setInitMethodName(initMethod);
}

AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
registry.registerBeanDefinition(annotatedMethod.getMethodName(), beanDefinition);
}
}
}

在构造 BeanDefinition 时调用了 setAutowireMode() 方法设置注入模式,这是因为在自定义的 Config 类中有一特殊的被 @Bean 标记的方法:

1
2
3
4
5
6
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}

接收一个 DataSource 类型的参数,需要将容器中这个类型的 Bean 进行注入,设置的 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR 注入模式则能完成这个功能。

1
2
3
4
5
6
7
18:10:51.353 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦
config
daidawang.advancedSpring.bean.a05.DDWBeanPostProcessor
bean1
sqlSessionFactoryBean
dataSource
18:10:51.453 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-0} closing ...

模拟实现注入@Mapper

@Mapper 注解写在接口上, 而根据前面内容得知,@Mapper 被解析后在 Spring 容器中也存在与被标记的接口相关的 Bean。

难道 Spring 能管理接口?

那肯定是不行的,Spring 只能管理对象这是毋庸置疑的。那这些接口是怎么变成对象被 Spring 管理的呢?

这依赖于 MapperFactoryBean 将接口转换为对象。

在 Config 添加注册 mapper1MapperFactoryBean 和 mapper2MapperFactoryBean 的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public MapperFactoryBean<Mapper1> mapper1MapperFactoryBean(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean<>(Mapper1.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}

@Bean
public MapperFactoryBean<Mapper2> mapper2MapperFactoryBean(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}

再运行 main() 方法可以看到容器中存在名为 mapper1MapperFactoryBean 和 mapper2MapperFactoryBean 的 Bean。

这种方式虽然可以完成 Mapper 接口的注册,但每次只能单个注册,不能批量注册。

移除 Config 类中的 mapper1() 和 mapper2() 方法,自行编写 BeanDefinitionRegistryPostProcessor 接口的实现类完成 @Mapper 注解的解析:

按照 模拟实现组件扫描 ComponentSacnPostProcessor 的方式,我先在A05Application 类上添加了 @MapperScan(“daidawang.advancedSpring.bean.a05.mapper”)

然后开始编写MapperPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
@SneakyThrows
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
MapperScan annotation = AnnotationUtils.findAnnotation(A05Application.class, MapperScan.class);
if (annotation != null){
String[] value = annotation.value();
String packageName = value[0];
String path = "classpath*:" + packageName.replace(".", "/") + "/*.class";
//扫描mapper包下的资源
for (Resource resource : new PathMatchingResourcePatternResolver().getResources(path)) {
MetadataReader reader = factory.getMetadataReader(resource);
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//判断是否有@Mapper注解并且是接口
if (annotationMetadata.hasAnnotation(Mapper.class.getName()) && annotationMetadata.isInterface()) {
//创建BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
String beanName = generator.generateBeanName(beanDefinition, registry);
//注册BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);
}
}

1
2
3
4
5
6
public static void main(String[] args) {
// --snip--
context.registerBean(MapperPostProcessor.class);
context.refresh();

for (String name : contBean
1
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [daidawang.advancedSpring.bean.a05.mapper.Mapper1]: Specified class is an interface

抛出 Mapper1 是一个接口的异常,正如前文提到的 Spring 无法管理接口,上文是通过创建MapperFactoryBean MapperFactoryBean 来实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public MapperFactoryBean<Mapper1> mapper1MapperFactoryBean(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean<>(Mapper1.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}

@Bean
public MapperFactoryBean<Mapper2> mapper2MapperFactoryBean(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}

所以当创建 BeanDefinitionean 对象的时候,也需要创建 MapperFactoryBean 类型的BeanDefinitionean .

而 BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()) 这样就创建了 Mappper 类型的 BeanDefinitionean ,

为了创建MapperFactoryBean 型的BeanDefinitionean 做出如下修改

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

// --snip--
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
.getBeanDefinition();
String beanName = generator.generateBeanName(beanDefinition, registry);
//注册BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
1
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mapperFactoryBean': Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required

好了已经创建了 MapperFactoryBean 类性的 BeanDefinitionean 对象,

但是由于没有给定参数SqlSessionFactory 而抛出异常 。

添加.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

// --snip--
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
String beanName = generator.generateBeanName(beanDefinition, registry);
//注册BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
1
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mapperFactoryBean': Property 'mapperInterface' is required

由于MapperFactoryBean 的构造是 Mapper 类的类路径

MapperFactoryBean mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);

所以添加 .addConstructorArgValue(annotationMetadata.getClassName())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

// --snip--
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(annotationMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
String beanName = generator.generateBeanName(beanDefinition, registry);
//注册BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
1
2
3
4
5
6
7
8
9
08:03:41.154 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦
config
daidawang.advancedSpring.bean.a05.DDWBeanPostProcessor
daidawang.advancedSpring.bean.a05.MapperPostProcessor
bean1
sqlSessionFactoryBean
dataSource
mapperFactoryBean
08:03:41.342 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-0} closing ...

观察控制台发现 只打印了 mapperFactoryBean ,没有打印 Mapper1 与 Mapper2

这是因为 通过AnnotationBeanNameGenerator 创建 beanName 的时候,

默认是 beanDefinition 的名字,而 beanDefinition 已经被改为了 MapperFactoryBean.class

在注册 Mapper1 时往容器添加了 mapperFactoryBean ,在注册 Mapper2 时 又添加了相同名字的 mapperFactoryBean ,导致 Mapper1 Mapper2 在容器里共用了同一个也名字

我们可以借用源码的思路用二级缓存来解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package daidawang.advancedSpring.bean.a05;

import lombok.SneakyThrows;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.lang.invoke.VarHandle;

/**
* @version v1.0
* @ClassName: MapperPostProcessor
* @Description: 自定义的MapperScan 后置处理器,
* @Author: 呆大王
* @Date: 2024/8/18
*/
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
@SneakyThrows
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
MapperScan annotation = AnnotationUtils.findAnnotation(A05Application.class, MapperScan.class);
if (annotation != null){
String[] value = annotation.value();
String packageName = value[0];
String path = "classpath*:" + packageName.replace(".", "/") + "/*.class";
//扫描mapper包下的资源
for (Resource resource : new PathMatchingResourcePatternResolver().getResources(path)) {
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
//判断是否有@Mapper注解并且是接口
if (annotationMetadata.hasAnnotation(Mapper.class.getName()) && annotationMetadata.isInterface()) {
//创建BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class)
//在构造 MapperFactoryBean 实例时传递一个构造函数参数,即被扫描类的全限定名
//MapperFactoryBean 类有一个构造函数接受一个 Class 参数,代表了要创建的 Mapper 接口。
.addConstructorArgValue(annotationMetadata.getClassName())
//设置了 MapperFactoryBean 的自动装配模式为 AUTOWIRE_BY_TYPE。这意味着 Spring 会根据类型自动注入 MapperFactoryBean 需要的依赖。
//MapperFactoryBean 需要一个 SqlSessionFactory 或 SqlSessionTemplate 来执行 SQL 查询
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();

AbstractBeanDefinition definition = BeanDefinitionBuilder
.genericBeanDefinition(annotationMetadata.getClassName())
.getBeanDefinition();
String beanName = generator.generateBeanName(definition, registry);

//注册BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);
}
}
1
2
3
4
5
6
7
8
9
10
08:19:25.224 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦
config
daidawang.advancedSpring.bean.a05.DDWBeanPostProcessor
daidawang.advancedSpring.bean.a05.MapperPostProcessor
bean1
sqlSessionFactoryBean
dataSource
mapper1
mapper2
08:19:25.393 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-0} closing ...

成功自定义 Mapper 后置处理器, 当然我这里是通过 @MapperScan 注解来完成的,也可以和老师一样手动拼写类路径。

46 . @Value 注入底层

46.1 解析@Value

在第四讲黑马四十九讲容器与Bean** 3.2@Autowired@Value后置处理器 **中给工厂设置了 <font style="background-color:rgba(255, 255, 255, 0);">ContextAnnotationAutowireCandidateResolver</font>类,来深入了解一下吧。

1
2
3
4
5
6
public class Bean1{
@Value("${JAVA_HOME}")
private String home;
@Value("18")
private int age;
}

这里的 JAVA_HOME 由系统环境变量填充,18 为整型。

解析 @Value需要两步:

  1. 获取 @Value注解中的值
  2. 解析属性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@SuppressWarnings("all")
@Configuration
public class A46 {
@SneakyThrows
public static void main(String[] args) {
/**
* 创建一个新的AnnotationConfigApplicationContext,从给定的组件类中派生bean定义,并自动刷新上下文。
* @param componentClasses 一个或多个组件类,例如@Configuration类
*/
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46.class); DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
beanFactory.setAutowireCandidateResolver(resolver);

Field home = Bean1.class.getDeclaredField("home");
text1(home, resolver, context);
// text2(Bean1.class.getDeclaredField("age"), resolver, context);

}

private static void text1(Field field, ContextAnnotationAutowireCandidateResolver resolver, AnnotationConfigApplicationContext context) {
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(field, Boolean.FALSE);
//获取@Value的内容
String value = resolver.getSuggestedValue(dependencyDescriptor).toString();
System.out.println(value);
//解析${}
value= context.getEnvironment().resolvePlaceholders(value);

System.out.println(value);
}
}
1
2
${JAVA_HOME}
D:\software\work\jdk\jdk8\jdk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void text2(Field field, ContextAnnotationAutowireCandidateResolver resolver, AnnotationConfigApplicationContext context) {
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(field, Boolean.FALSE);
//获取@Value的内容
String value = resolver.getSuggestedValue(dependencyDescriptor).toString();
System.out.println("@Value 的 value 属性值: " + value);
//解析${}
value= context.getEnvironment().resolvePlaceholders(value);

System.out.println("解析得到的值: " + value);
System.out.println("解析得到的值的类型: " + value.getClass());
//此处 Spring中有两套转换接口,TypeConverter是底层的只做类型转换不做数据绑定的,而PropertyEditor是做数据绑定的(23讲未完成)
Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, dependencyDescriptor.getDependencyType());
System.out.println("转换后的类型: " + age.getClass());
}
1
2
3
4
@Value 的 value 属性值: 18
解析得到的值: 18
解析得到的值的类型: class java.lang.String
转换后的类型: class java.lang.Integer

46.2 EL表达式的解析

@Value 里面还可以写 EL表达式: @Value("#{@bean3}"),表示根据名称找到 bean3 的 bean 同 @Autowired

EL 表达式全称:SpringEL ,简称:SpEL, 用于 #{} 之间

新建如下几个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Bean2 {
@Value("#{@bean3}")
private Bean3 bean3;
}

@Component("bean3")
public class Bean3 {
}

static class Bean4 {
@Value("#{'hello, ' + '${JAVA_HOME}'}")
private String value;
}

如果继续使用 test2() 方法解析 Bean2 中的 @Value 注解

1
2
3
4
public static void main(String[] args) {
// --snip--
text2(Bean2.class.getDeclaredField("bean3"), resolver, context);
}

会出现:

1
2
3
4
@Value 的 value 属性值: #{@bean3}
解析得到的值: #{@bean3}
解析得到的值的类型: class java.lang.String
Failed to convert value of type 'java.lang.String' to required type 'dai.dawang.sundy.a46Value注解的原理.A46$Bean3'; Cannot convert value of type 'java.lang.String' to required type 'dai.dawang.sundy.a46Value注解的原理.A46$Bean3': no matching editors or conversion strategy found

是最后一步类型转换出错, 无法将 String转换为 A46$Bean3。

为了解析成功,需要在转换前解析 EL 表达式 #{}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static void main(String[] args) {
// --snip--
//text2(Bean2.class.getDeclaredField("bean3"), resolver, context);
test3(context, resolver, Bean2.class.getDeclaredField("bean3"));
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
text3(Bean4.class.getDeclaredField("value"), resolver, context);
}

/**
* 获取@Value中EL表达式的内容
* @param field
* @param resolver
* @param context
*/
private static void text3(Field field, ContextAnnotationAutowireCandidateResolver resolver, AnnotationConfigApplicationContext context) {
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(field, Boolean.FALSE);
//获取@Value的内容
String value = resolver.getSuggestedValue(dependencyDescriptor).toString();
System.out.println("@Value 的 value 属性值: " + value);
//解析${}
value= context.getEnvironment().resolvePlaceholders(value);
System.out.println("解析得到的值: " + value);
System.out.println("解析得到的值的类型: " + value.getClass());

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//解析#{},解析EL表达式
Object bean3 = beanFactory.getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(beanFactory, null));

//类型转换
Object result = beanFactory.getTypeConverter().convertIfNecessary(bean3,dependencyDescriptor.getDependencyType() );
System.out.println("转换后的类型: " + result.getClass());
}
1
2
3
4
5
6
7
8
9
@Value 的 value 属性值: #{@bean3}
解析得到的值: #{@bean3}
解析得到的值的类型: class java.lang.String
转换后的类型: class dai.dawang.sundy.a46Value注解的原理.A46$Bean3
>>>>>>>>>>>>>>>>>>>>>>>>>
@Value 的 value 属性值: #{'hello, ' + '${JAVA_HOME}'}
解析得到的值: #{'hello, ' + 'D:\software\work\jdk\jdk8\jdk'}
解析得到的值的类型: class java.lang.String
转换后的类型: class java.lang.String

6. Aware 接口

允许 Bean 在初始化过程中获取 Spring 容器中的某些组件或信息.

Aware 接口

Aware 接口用于注入一些与容器相关的信息,比如:

  • BeanNameAware 注入 Bean 的名字
  • BeanFactoryAware 注入 BeanFactory 容器
  • ApplicationContextAware 注入 ApplicationContext 容器
  • EmbeddedValueResolverAware 解析 ${}
  • InitializingBean :初始化方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author mofan
* @date 2023/1/8 16:12
*/
@Slf4j
public class MyBean implements BeanNameAware, ApplicationContextAware {
@Override
public void setBeanName(String name) {
log.info("当前 Bean: " + this + "名字叫: " + name);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("当前 Bean: " + this + "容器是: " + applicationContext);
}
}
1
2
3
4
5
6
7
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);

context.refresh();
context.close();
}
1
2
当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 名字叫:daidawang.advancedSpring.bean.a06.MyBean
当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 容器对象是:org.springframework.context.support.GenericApplicationContext@6615435c, started on Sun Aug 18 17:31:44 CST 2024

InitialzingBean

1
2
3
4
5
6
7
8
9
@Slf4j
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
// --snip--

@Override
public void afterPropertiesSet() throws Exception {
log.info("当前 Bean: " + this + " 初始化");
}
}
1
2
3
当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 名字叫:daidawang.advancedSpring.bean.a06.MyBean
当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 容器对象是:org.springframework.context.support.GenericApplicationContext@6615435c, started on Sun Aug 18 17:31:44 CST 2024
当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 初始化

当同时实现 Aware 接口和 InitializingBean 接口时,会先执行 Aware 接口。

BeanFactoryAware 、ApplicationContextAware 和 EmbeddedValueResolverAware 三个接口的功能可以使用 @Autowired 注解实现,InitializingBean 接口的功能也可以使用 @PostConstruct 注解实现,为什么还要使用接口呢?

@Autowired 和 @PostConstruct 注解的解析需要使用 Bean 后置处理器,属于拓展功能,而这些接口属于内置功能,不加任何拓展 Spring 就能识别。在某些情况下,拓展功能会失效,而内容功能不会失效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Slf4j
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
// --snip--

@Autowired
public void setApplicationContextWithAutowired(ApplicationContext applicationContext) {
log.info("当前 Bean: " + this + " 使用 @Autowired 注解,容器是: " + applicationContext);
}

@PostConstruct
public void init() {
log.info("当前 Bean: " + this + " 使用 @PostConstruct 注解初始化");
}
}

再运行 main() 方法会发现使用的注解没有被成功解析,原因很简单,GenericApplicationContext 是一个干净的容器,其内部没有用于解析这些注解的后置处理器。如果想要这些注解生效,则需要像前文一样添加必要的后置处理器:

1
2
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);

@Autowired 的失效情况

在某些情况下,尽管容器中存在必要的后置处理器,但 @Autowired 和 @PostConstruct 注解也会失效。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Slf4j
@Configuration
public class MyConfig1 {
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
log.error("注入 ApplicationContext");
}

@PostConstruct
public void init() {
log.error("初始化");
}
}
1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myConfig1", MyConfig1.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
// 解析配置类中的注解
context.registerBean(ConfigurationClassPostProcessor.class);

context.refresh();
context.close();
}
1
2
18:16:39.141 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 注入 ApplicationContext
18:16:43.666 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 初始化

@Autowired 和 @PostConstruct 注解成功被解析。

如果再对 Config1 进行一点小小的修改呢?

1
2
3
4
5
6
7
8
9
10
@Slf4j
@Configuration
public class MyConfig1 {
// --snip--

@Bean
public BeanFactoryPostProcessor processor1() {
return processor -> log.info("执行 processor1");
}
}

在 Config1 中添加了一个被 @Bean 注解标记的 processor1() 方法,用于向容器中添加 BeanFactoryPostProcessor。

如果再运行 main() 方法:

1
18:16:43.693 [main] INFO daidawang.advancedSpring.bean.a06.Config2 -- 执行 processor1

processor1() 方法成功生效,但 @Autowired 和 @PostConstruct 注解的解析失败了。

对于 context.refresh(); 方法来说,它主要按照以下顺序干了三件事:

  1. 执行 BeanFactory 后置处理器;
  2. 添加 Bean 后置处理器;
  3. 创建和初始化单例对象。

比如当 Java 配置类不包括 BeanFactoryPostProcessor 时:

BeanFactoryPostProcessor 会在 Java 配置类初始化之前执行。

当 Java 配置类中定义了BeanFactoryPostProcessor 时,如果要创建配置类中的 BeanFactoryPostProcessor 就必须 提前 创建和初始化 Java 配置类。

在创建和初始化 Java 配置类时,由于 BeanPostProcessor 还未准备好,无法解析配置类中的 @Autowired 等注解,导致 @Autowired 等注解失效:

要解决这个问题也很简单,使用相关接口的功能实现注入和初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
@Override
public void afterPropertiesSet() throws Exception {
log.info("初始化");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("注入 ApplicationContext");
}

@Bean
public BeanFactoryPostProcessor processor2() {
return processor -> log.info("执行 processor2");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myConfig2", MyConfig2.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);

// 解析配置类中的注解
context.registerBean(ConfigurationClassPostProcessor.class);

context.refresh();
context.close();
}

1
2
3
18:16:39.141 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 注入 ApplicationContext
18:16:43.666 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 初始化
18:16:43.693 [main] INFO daidawang.advancedSpring.bean.a06.Config2 -- 执行 processor2

总结 Aware

  1. Aware 接口提供了一种 内置 的注入手段,可以注入 BeanFactory、ApplicationContext;
  2. InitializingBean 接口提供了一种 内置 的初始化手段;
  3. 内置的注入和初始化不受拓展功能的影响,总会被执行,因此 Spring 框架内部的类总是使用这些接口。

7. 初始化与销毁

初始化

spring 支持 3 种初始化方式

  1. @PostConstruct ,后置处理器,执行顺序第一
  2. 实现InitializingBean ,内置处理器,执行顺序第二
  3. @Bean(initMethod = “initMethod”),BeanDefinition 指定的,执行顺序第三

创建 Bean1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @version v1.0
* @ClassName: Bean1
* @Description: 测试初始化顺序
* @Author: 呆大王
* @Date: 2024/8/18
*/
@Slf4j
public class Bean1 implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean初始化");
}

@PostConstruct
public void init() {
System.out.println("PostConstruct初始化");
}

public void initMethod() {
System.out.println("initMethod初始化");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @version v1.0
* @ClassName: A07Application
* @Description: 初始化与销毁
* @Author: 呆大王
* @Date: 2024/8/18
*/
@SpringBootApplication
public class A07Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args);
context.close();
}

@Bean(initMethod = "initMethod")
public Bean1 bean1() {
return new Bean1();
}
}

1
2
3
PostConstruct初始化
InitializingBean初始化
initMethod初始化

销毁

spring 支持 3 种销毁方式

  1. @PreDestroy ,后置处理器,执行顺序第一
  2. 实现 DisposableBean ,内置处理器,执行顺序第二
  3. @Bean(destroyMethod = “destroyMethod”),BeanDefinition 指定的,执行顺序第三

创建 Bean2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @version v1.0
* @ClassName: Bean2
* @Description: 测试销毁顺序
* @Author: 呆大王
* @Date: 2024/8/18
*/
public class Bean2 implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("destroy销毁");
}

@PreDestroy
public void deprecated() {
System.out.println("@PreDestroy销毁");
}

public void destroyMethod() {
System.out.println("destroyMethod销毁");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SpringBootApplication
public class A07Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args);
context.close();
}

@Bean(initMethod = "initMethod")
public Bean1 bean1() {
return new Bean1();
}
@Bean(destroyMethod = "destroyMethod")
public Bean2 Bean2() {
return new Bean2();
}
}

1
2
3
@PreDestroy销毁
destroy销毁
destroyMethod销毁

Aware 与内外置处理器顺序

添加 Aware 接口到 Bean1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package daidawang.advancedSpring.bean.a07初始化与销毁;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;

/**
* @version v1.0
* @ClassName: Bean1
* @Description: 测试初始化顺序
* @Author: 呆大王
* @Date: 2024/8/18
*/
@Slf4j
public class Bean1 implements InitializingBean, BeanNameAware, ApplicationContextAware, BeanFactoryAware, EmbeddedValueResolverAware {


@PostConstruct
public void init() {
System.out.println("PostConstruct初始化");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean初始化");
}

public void initMethod() {
System.out.println("initMethod初始化");
}

@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware执行");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware执行");
}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware执行");

}

@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
System.out.println("EmbeddedValueResolverAware执行");
}
}

1
2
3
4
5
6
7
BeanNameAware执行
BeanFactoryAware执行
EmbeddedValueResolverAware执行
ApplicationContextAware执行
PostConstruct初始化
InitializingBean初始化
initMethod初始化

这些初始化方式的执行顺序:

  1. BeanNameAware:这是最早执行的 Aware 接口之一,因为它在 Bean 的实例化之后立即调用。
  2. ApplicationContextAware:在BeanNameAware之后执行,在Bean的实例化之后,但在依赖注入之前。
  3. BeanFactoryAware:同样在依赖注入之前执行。
  4. EmbeddedValueResolverAware:在依赖注入之前执行,用于解析 ${} 表达式中的占位符。
  5. 依赖注入:Spring 容器会进行依赖注入,即注入 @Autowired 标注的字段或方法。
  6. @PostConstruct:在依赖注入之后执行。
  7. InitializingBean.afterPropertiesSet():在依赖注入完成之后执行,通常在 @PostConstruct 之后。
  8. @Bean(initMethod = “initMethod”):在 afterPropertiesSet() 方法之后执行

8. Scope

一个 bean 没有指定@Scope注解,默认是单例的。

Scope 类型

  • singleton 单例,ApplicationContest 创建而创建,ApplicationContest 销毁而销毁
  • prototype 多例, 使用时创建,手动销毁
  • request 存在于 request 域中,生命周期同request 域
  • sesion 会话域,会话开始 bean 创建,会话结束 bean 销毁
  • application 应用程序域,应用程序开始 bean 创建,Spring 没有实现 销毁,WebServerContext

验证request 、sesion、application 做出如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
public class MyController {
@Lazy
@Autowired
private BeanForApplication beanForApplication;
@Lazy
@Autowired
private BeanForRequest beanForRequest;
@Lazy
@Autowired
private BeanForSession beanForSession;
//Error creating bean with name 'beanForRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton
@GetMapping("/test")
public String test(HttpServletRequest request, HttpSession session){
// 设置 session 过期时间为 10 秒
session.setMaxInactiveInterval(10);
// ServletContext sc = request.getServletContext();
return "<ul>" +
"<li>request scope: " + beanForRequest + "</li>" +
"<li>session scope: " + beanForSession + "</li>" +
"<li>application scope: " + beanForApplication + "</li>" +
"</ul>";
}
}

如果不添加@Lazy 注解会抛出注释的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Slf4j
@Component
@Scope("application")
public class BeanForApplication {
@PreDestroy
public void destroy() {
log.info("销毁BeanForApplication");
}
}
@Slf4j
@Component
@Scope("request")
public class BeanForRequest {
@PreDestroy
public void destroy() {
log.info("销毁BeanForRequest");
}
}
@Slf4j
@Component
@Scope("session")
public class BeanForSession {
@PreDestroy
public void destroy() {
log.info("销毁BeanForSession");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @version v1.0
* @ClassName: A08Application
* @Description:
* @Author: 呆大王
* @Date: 2024/8/18
*/
@SpringBootApplication
public class A08Application {
public static void main(String[] args) {
// 启动Spring应用
SpringApplication.run(A08Application.class, args);
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/advanced_spring");
dataSource.setName("root");
dataSource.setPassword("123456");
return dataSource;
}
}

启动后访问

http://localhost:8080/test

每次刷新页面,重新请求 request scope 都会改变。同时控制台打印

1
INFO 3212 --- [nio-8080-exec-2] d.a.bean.a08Scope.BeanForRequest      : 销毁BeanForRequest

同一个浏览器无论发送多少次请求,都属于一个会话( session 域),所以 BeanForSession 不会改变。

同一个 web 程序也不会改变.。

在打开一个新浏览器访问

会发现 会话( session 域)有所改变

而 web 程序仍然不会改变:

在 Singleton 中使用其他类型的注意事项

一个单例的 bean 注入其他 scope 类型的 bean 会出现问题,

例如在单例 bean 里引用 多例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class E {
@Getter
@Autowired
private F1 f1;
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class F1 {
@PreDestroy
public void destroy() {
System.out.println("销毁F1");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j
@ComponentScan("daidawang.advancedSpring.bean.a09Scope")
public class A09Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09Application.class); context.registerBean(ConfigurationClassPostProcessor.class);
E e = context.getBean(E.class);
log.info("{}", e.getF1());
log.info("{}", e.getF1());
log.info("{}", e.getF1());
context.close();
}
}

多次调用获取多例 F

1
2
3
A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@58ea606c
A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@58ea606c
A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@58ea606c

scope 失效解决

却发现 F 是同一个,明明在 F 类上指定了多例@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

这是因为对于单例对象来讲,依赖注入仅发生一次,后续再没有用到多例 F,E 用的始终是第一次依赖注入 F

下面是解决方法,理念上殊途同归:都是延迟其他 scope bean 的获取,而不是在单例 bean 里直接获取其他 scope bean 对象

@Lazy 注解解决失效

用代理去获取多例

解决

通过 懒加载 @Lazy ,代理对象虽然还是同一个,但是每次使用代理对象的任意方法时

由代理创建新 F 对象

在 E 类里添加@Lazy

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class E {

@Autowired
@Lazy
public void setF(F f) {
this.f = f;
log.info("setF(F f) {}", f.getClass());
}

// --snip--
}
1
2
3
A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@543295b0
A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@74e28667
A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@663c9e7a

指定 proxyMode 类型解决

用代理去获取多例

创建 F2 并指定 proxyMode 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @version v1.0
* @ClassName: F2
* @Description: 指定proxyMode属性解决作用域失效问题
* @Author: 呆大王
* @Date: 2024/8/18
*/
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class F2 {
@PreDestroy
public void destroy() {
System.out.println("销毁F1");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
@ComponentScan("daidawang.advancedSpring.bean.a09Scope")
public class A09Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09Application.class);
context.registerBean(ConfigurationClassPostProcessor.class);
E e = context.getBean(E.class);
// log.info("{}", e.getF1());
// log.info("{}", e.getF1());
// log.info("{}", e.getF1());

log.info("{}", e.getF2());
log.info("{}", e.getF2());
log.info("{}", e.getF2());
context.close();
}
}

1
2
3
A09Application -- daidawang.advancedSpring.bean.a09Scope.F2@c05fddc
A09Application -- daidawang.advancedSpring.bean.a09Scope.F2@5a5a729f
A09Application -- daidawang.advancedSpring.bean.a09Scope.F2@4b520ea8

ObjectFactory 获取多例对象

用工厂去获取多例

创建 F3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @version v1.0
* @ClassName: F3
* @Description: 通过ObjectProxy解决作用域失效问题
* @Author: 呆大王
* @Date: 2024/8/18
*/
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class F3 {
@PreDestroy
public void destroy() {
System.out.println("销毁F3");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class E {
@Lazy
@Getter
@Autowired
private F1 f1;

@Autowired
@Getter
private F2 f2;

@Autowired
private ObjectFactory<F3> objectFactory;

public F3 getF3() {
return objectFactory.getObject();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Slf4j
@ComponentScan("daidawang.advancedSpring.bean.a09Scope")
public class A09Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09Application.class);
context.registerBean(ConfigurationClassPostProcessor.class);
E e = context.getBean(E.class);
// log.info("{}", e.getF1());
// log.info("{}", e.getF1());
// log.info("{}", e.getF1());

// log.info("{}", e.getF2());
// log.info("{}", e.getF2());
// log.info("{}", e.getF2());

log.info("{}", e.getF3());
log.info("{}", e.getF3());
log.info("{}", e.getF3());
context.close();
}
}
1
2
3
A09Application -- daidawang.advancedSpring.bean.a09Scope.F3@4201c465
A09Application -- daidawang.advancedSpring.bean.a09Scope.F3@2de56eb2
A09Application -- daidawang.advancedSpring.bean.a09Scope.F3@5745ca0e

ApplicationContext

用容器去获取多例

创建 F4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package daidawang.advancedSpring.bean.a09Scope;

import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
* @version v1.0
* @ClassName: F4
* @Description: 通过ApplicationContext解决
* @Author: 呆大王
* @Date: 2024/8/18
*/
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class F4 {
@PreDestroy
public void destroy() {
System.out.println("销毁F4");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class E {

//--- snip
@Autowired
private ApplicationContext applicationContext;

public F3 getF3() {
return objectFactory.getObject();
}

public F4 getF4() {
return applicationContext.getBean(F4.class);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Slf4j
@ComponentScan("daidawang.advancedSpring.bean.a09Scope")
public class A09Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09Application.class);
context.registerBean(ConfigurationClassPostProcessor.class);
E e = context.getBean(E.class);
// log.info("{}", e.getF1());
// log.info("{}", e.getF1());
// log.info("{}", e.getF1());

// log.info("{}", e.getF2());
// log.info("{}", e.getF2());
// log.info("{}", e.getF2());

// log.info("{}", e.getF3());
// log.info("{}", e.getF3());
// log.info("{}", e.getF3());

log.info("{}", e.getF4());
log.info("{}", e.getF4());
log.info("{}", e.getF4());
context.close();
}
}
1
2
3
A09Application -- daidawang.advancedSpring.bean.a09Scope.F4@43f02ef2
A09Application -- daidawang.advancedSpring.bean.a09Scope.F4@338fc1d8
A09Application -- daidawang.advancedSpring.bean.a09Scope.F4@4722ef0c

9. 补充

比反射快的方式: 读取资源元数据创建一个CachingMetadataReaderFactory实例

context.refresh(); 方法来说,它主要按照以下顺序干了三件事:

  1. 执行 BeanFactory 后置处理器;
  2. 添加 Bean 后置处理器;
  3. 创建和初始化单例对象。