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, 示例中的对象 >folded1 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 管理"); } }
@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
等注解。
与 mybatis 整合时需要使用到的 BeanFactoryPostProcessor:MapperScannerConfigurer
,它的作用是扫描 mybatis 的 @Mapper
,将对应的 Mapper 接口解析成 BeanDefinition[1] 补充到 BeanFactory 中。
Click to expand the code, 示例中的Mapper >folded1 2 3 4 5
| @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