BeanFactoryPostProcessor
2025-01-22 08:19:30    854 字   
This post is also available in English and alternative languages.

BeanFactoryPostProcessor 是 Spring 框架中的一个重要接口,它允许在实际创建 Bean 之前对 Spring 容器进行额外的配置。本文将更详细地了解 BeanFactoryPostProcessor 是什么,它的作用以及如何使用它。

Spring Framework版本:5.1.6.RELEASE (点击跳转官方文档)


1. 什么是 BeanFactoryPostProcess

BeanFactoryPostProcessor 是一个接口,它提供了一个钩子,允许在将 BeanDefinition[1] 转换为实际的 bean 实例之前修改 BeanDefinition[1]


2. BeanFactoryPostProcessor 作用示例

示例使用 GenericApplicationContext,这个容器相对来说是「干净」的,它没有像 AnnotationConfigApplicationContext 那样内置一些 BeanPostProcessor,这样便于后续测试观察。


2.1. ConfigurationClassPostProcessor

Click to expand the code, 示例中的对象 >folded
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
@ComponentScan("com.yxcheng.example99.a06.component")
public static 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/test");
dataSource.setUsername("admin");
dataSource.setPassword("admin");
return dataSource;
}
}

@Component
public class Bean1 {
public Bean1() { System.out.println("bean1 已被 spring 管理"); }
}

// package com.yxcheng.example99.a06.component;
@Component
public class Bean2 {
public Bean2() { System.out.println("bean2 已被 spring 管理"); }
}
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName: " + beanDefinitionName);
}
context.close();
}

上面代码中,使用 GenericApplicationContext 创建了一个容器,并手动向容器中注册 Config 类。接着输出容器中所有的 BeanDefinition[1] 名称,然后在 context.refresh() 方法中,容器会初始化所有单例。最后再手动关闭容器。

运行结果如下:

1
beanDefinitionName: config

从结果看,Config 类上的 @ComponentScan@Bean 注解都没有解析生效。


为了使 Config 类上的 @ComponentScan@Bean 注解都正确解析并生效,手动向容器中添加一个 BeanFactoryPostProcessor: ConfigurationClassPostProcessor

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName: " + beanDefinitionName);
}
context.close();
}

运行结果如下:

1
2
3
4
5
6
7
8
bean2 已被 spring 管理
bean1 已被 spring 管理
beanDefinitionName: config
beanDefinitionName: org.springframework.context.annotation.ConfigurationClassPostProcessor
beanDefinitionName: bean2
beanDefinitionName: bean1
beanDefinitionName: sqlSessionFactoryBean
beanDefinitionName: dataSource

添加 ConfigurationClassPostProcessor 这个 BeanFactoryPostProcessor 之后,输出的 BeanDefinition[1] 多了不少。Bean2 来自于 @ComponentScan 注解组件扫描,Bean1、sqlSessionFactoryBean、dataSource 则来自于 @Bean 注解的处理。

ConfigurationClassPostProcessor 可以解析处理 @ComponentScan@Bean@Import@ImportResource 等注解。


2.2. MapperScannerConfigurer

与 mybatis 整合时需要使用到的 BeanFactoryPostProcessor:MapperScannerConfigurer,它的作用是扫描 mybatis 的 @Mapper,将对应的 Mapper 接口解析成 BeanDefinition[1] 补充到 BeanFactory 中。

Click to expand the code, 示例中的Mapper >folded
1
2
3
4
5
// com.yxcheng.example99.a06.mapper
@Mapper
public interface Mapper1 { }
@Mapper
public interface Mapper2 { }

使用 MapperScannerConfigurer 时,需要为它指定扫描的包路径。

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(ConfigurationClassPostProcessor.class);
context.registerBean(MapperScannerConfigurer.class, bd -> {
bd.getPropertyValues().add("basePackage", "com.yxcheng.example99.a06.mapper");
});
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println("beanDefinitionName: " + beanDefinitionName);
}
context.close();
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bean2 已被 spring 管理
bean1 已被 spring 管理
beanDefinitionName: config
beanDefinitionName: org.springframework.context.annotation.ConfigurationClassPostProcessor
beanDefinitionName: org.mybatis.spring.mapper.MapperScannerConfigurer
beanDefinitionName: bean2
beanDefinitionName: bean1
beanDefinitionName: sqlSessionFactoryBean
beanDefinitionName: dataSource
beanDefinitionName: mapper1
beanDefinitionName: mapper2
beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName: org.springframework.context.event.internalEventListenerFactory

相较上面的运行结果,多了 mapper1、mapper2。而且 MapperScannerConfigurer 的内部会间接的帮我们加上 Spring 内部其他的 BeanFactoryPostProcessor。


3. Reference


  1. 1.Bean元信息的接口,它包含了Bean的Class类型、属性、依赖关系等信息。