ConfigurationClassPostProcessor
提到过,bean 工厂的后处理器可以用于处理 bean 定义。比如下面的示例:
static class Bean1{}
basePackages = "cn.icexmoon.demo.bean")
(static class Config{
public Bean1 bean1(){
return new Bean1();
}
}
bean1 由配置类的 bean 方法添加。bean2
位于包cn.icexmoon.demo.bean
,由@componentScan
开启的包扫描添加:
public class Bean2 {
}
创建容器并添加配置类:
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.registerBean(Config.class);
ctx.refresh();
printBeanDefinitionNames(ctx);
ctx.close();
执行后会发现容器中只有配置类的 bean 定义,缺少 bean1 和 bean2 的定义。
这个问题可以用工厂后处理器解决:
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.registerBean(Config.class);
ctx.registerBean(ConfigurationClassPostProcessor.class);
ctx.refresh();
printBeanDefinitionNames(ctx);
ctx.close();
工厂后处理器ConfigurationClassPostProcessor
除了可以处理@Configuration
、@ComponentScan
、@Bean
注解以外,还可以处理@Import
和@ImportResource
注解:
basePackages = "cn.icexmoon.demo.bean")
(Bean3.class)
("classpath:bean4.xml")
(static class Config{
public Bean1 bean1(){
return new Bean1();
}
}
@Import
注解用于将配置类或普通类添加为 Bean 定义。
@ImportResource
用于从 XML 配置中加载 Bean 定义。
MapperScannerConfigurer
使用工厂后处理器MapperScannerConfigurer
可以自动扫描并添加 Mapper 到 bean 定义。
示例中有三个 Mapper (最后一个被有意错误定义为 class)位于包cn.icexmoon.demo.mapper
中。
Mapper 依赖于数据库连接池等,因此需要在配置类中添加相应的 Bean:
static class DbConfig{
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
druidDataSource.setUsername("root");
druidDataSource.setPassword("mysql");
return druidDataSource;
}
}
需要添加相关依赖:
mybatis-spring-boot-starter
spring-boot-starter-data-jdbc
mysql-connector-j
druid
使用工厂后处理器扫描并添加 Mapper:
GenericApplicationContext ctx = new GenericApplicationContext(); ctx.registerBean(DbConfig.class); ctx.registerBean(ConfigurationClassPostProcessor.class); ctx.registerBean(MapperScannerConfigurer.class, db->{ db.getPropertyValues().add("basePackage", "cn.icexmoon.demo.mapper"); }); ctx.refresh(); printBeanDefinitionNames(ctx); ctx.close();
为了指定在哪个包下扫描 Mapper,这里通过registerBean
的参数传入一个匿名函数,其参数类型为BeanDefinition
,可以利用它在共产后处理器创建时修改其属性,比如这里就是修改MapperScannerConfigurer
的 bean 属性basePackage
为指定的包。
原理
@ComponentScan
下面通过自定义一个处理@ComponentScan
注解的自定义工厂后处理器,来演示工厂后处理器的工作原理。
配置类:
basePackages = "cn.icexmoon.demo.bean2")
(static class Config {
}
包cn.icexmoon.demo.bean2
中包含三个类:
public class Bean1 {
}
public class Bean2 {
}
public class Bean3 {
}
自定义工厂后处理器:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 查找具有 @Configuration 注解的 配置类
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
String beanClassName = beanDefinition.getBeanClassName();
Class<?> beanClass = Class.forName(beanClassName);
// 判断类型是否有 @component 注解 和 @componentScan 注解
Configuration configuration = AnnotationUtils.findAnnotation(beanClass, Configuration.class);
ComponentScan componentScan = AnnotationUtils.findAnnotation(beanClass, ComponentScan.class);
if (configuration != null && componentScan != null) {
// 同时具有 @Component 注解和 @ComponentScan 注解
log.info("找到匹配的类" + beanClassName);
// 获取要处理的包
String[] basePackages = componentScan.basePackages();
for (String basePackage : basePackages) {
// 获取包对应的类路径,比如 classpath*:cn/icexmoon/demo/bean2/**/*.class
String classpath = "classpath*:%s/**/*.class".formatted(basePackage.replace('.', '/'));
log.info("classpath:" + classpath);
// 加载字节码文件(代码运行时没有源码,只有字节码)
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(classpath);
CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
// 要处理的类对应的字节码
log.info("class file:" + resource.getFilename());
// 读取类的元信息
MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
// 判断类是否有 @Component 注解或其子注解
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
// 获取要处理的字节码的类名
String className = metadataReader.getClassMetadata().getClassName();
// 添加类定义
// 方便起见,这里默认容器类型为 GenericApplicationContext
if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory) {
log.info("类[%s]被添加到容器中".formatted(className));
AbstractBeanDefinition bb = BeanDefinitionBuilder.genericBeanDefinition(className)
.setScope("singleton")
.getBeanDefinition();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
defaultListableBeanFactory.registerBeanDefinition(generator.generateBeanName(bb, defaultListableBeanFactory), bb);
}
}
}
}
}
}
}
代码比较多,但逻辑并不复杂:
-
获取要处理的配置类以及 @ComponentScan 注解
-
获取要处理的包名
-
根据包名获取到对应的字节码目录
-
获取对应的字节码文件
-
如果字节码文件有 @Component 相关注解,给工厂添加 bean 定义
这里借助两个工具类获取注解信息:
-
AnnotationUtils
,获取类中的注解信息 -
CachingMetadataReaderFactory
,获取资源文件(这里是字节码)中的注解信息
注意,这里使用的
AnnotationUtils
是 Spring 框架的,Junit 有一个同名类。
通过容器加载配置类和工厂后处理器:
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Config.class);
context.registerBean(ComponentScanPostProcessor.class);
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
context.close();
@Bean
下面演示怎么用自定义工厂后处理器处理配置类中的 bean 方法。
示例类型:
public class SingleConfig {
public Bean1 bean1(){
return new Bean1();
}
public Bean2 bean2(Bean1 bean1){
return new Bean2(bean1);
}
initMethod = "init")
( public Bean3 bean3(){
return new Bean3();
}
}
public class Bean1 { }
public class Bean2 {
private Bean1 bean1;
public Bean2(Bean1 bean1) {
this.bean1 = bean1;
}
}
public class Bean3 {
public void init(){
System.out.println("Bean3 init");
}
}
简单起见,这里没有再使用内部类作为配置类,而是单独的类。此外,配置类用 bean 方法定义了三个 bean,分别是普通的 bean,通过参数注入属性的 bean,以及指定方法作为初始化方法的 bean。
创建自定义工厂后处理器:
static class BeanMethodPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
String beanClassName = beanDefinition.getBeanClassName();
// 处理带 @Configuration 注解的 bean 定义
Class<?> beanClass = Class.forName(beanClassName);
Configuration configuration = AnnotationUtils.findAnnotation(beanClass, Configuration.class);
if (configuration != null) {
// 获取 bean 方法
CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
// 简单起见,这里默认所有配置类都是单独的配置类(非内部类)
String classpath = beanClassName.replace('.', '/') + ".class";
Resource resource = new PathMatchingResourcePatternResolver().getResource("classpath:" + classpath);
MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
Set<MethodMetadata> annotatedMethods = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata annotatedMethod : annotatedMethods) {
log.info(annotatedMethod.getMethodName());
// 构造 bean 定义
// 将 bean 方法用于 bean 创建的工厂方法
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition()
// 处理工厂方法参数注入
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.setFactoryMethodOnBean(annotatedMethod.getMethodName(), beanDefinitionName);
// 如果 bean 注解设置了 init 方法
String initMethod = (String) annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod");
if (initMethod != null && !initMethod.isEmpty()) {
beanDefinitionBuilder.setInitMethodName(initMethod);
}
AbstractBeanDefinition bd = beanDefinitionBuilder
.getBeanDefinition();
if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory) {
defaultListableBeanFactory.registerBeanDefinition(annotatedMethod.getMethodName(), bd);
}
}
}
}
}
}
将配置类和工厂后处理器添加到容器:
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(SingleConfig.class);
context.registerBean(BeanMethodPostProcessor.class);
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
context.close();
Mapper
如果要用”手动“的方式添加 Mapper 的 bean 定义:
static class DbConfig{
// ...
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean<>(Mapper1.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}
}
在项目中依次这样手动创建并不现实。因此可以用工厂的后处理器来扫描和创建。
static class MapperScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
// Mapper 所在包
private String basePackage;
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 在 bean 工厂加载了所有 bean 定义后调用
// 扫描并加载 Mapper
if (basePackage == null || basePackage.isEmpty()){
return;
}
// 从包路径扫描并加载字节码
String locationPatter = "classpath*:%s/**/*.class".formatted(basePackage.replace('.', '/'));
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(locationPatter);
CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
for (Resource resource : resources) {
// 获取元信息
MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
// 只处理接口
ClassMetadata classMetadata = metadataReader.getClassMetadata();
if(classMetadata.isInterface()){
// 构造 bean 定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
.getBeanDefinition();
// 生成名称
AbstractBeanDefinition beanNameDefinition = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName())
.getBeanDefinition();
AnnotationBeanNameGenerator annotationBeanNameGenerator = new AnnotationBeanNameGenerator();
String beanName = annotationBeanNameGenerator.generateBeanName(beanNameDefinition, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);
}
}
使用后处理器扫描 Mapper:
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(DbConfig2.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(MapperScanPostProcessor.class, bd -> {
bd.getPropertyValues().add("basePackage", "cn.icexmoon.demo.mapper");
});
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
context.close();
本文的完整示例可以从获取。
The End.
文章评论