Spring生命周期之BeanDefinitionRegistryPostProcessor

120 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

前言

Spring的使用当中,为了创建Bean对象,我们可以通过xml@Bean@Service等方式,但是有一种可以动态创建Bean对象的方式你知道嘛?那就是通过BeanDefinitionRegistryPostProcessor来动态创建BeanDefinition,通过BeanDefinition的注入从而达到Bean创建的动态化;

示例

下面我们通过一个简单的示例来看一下如何使用BeanDefinitionRegistryPostProcessor来动态创建Bean对象;

我们先创建出一个User类,这就是我们需要创建的Bean对象:

@Data
public class User {
private String name;
private int age;
}

下面我们来自定义一个BeanDefinitionRegistryPostProcessor子类:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.stereotype.Component;
​
/**
* @author zouwei
* @className CustomBeanDefinitionRegistryPostProcessor
* @date: 2022/12/14 11:54
* @description:
*/
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   // 给User类创建一个BeanDefinition
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(User.class.getName());
   // 设置属性值
beanDefinition.getPropertyValues().addPropertyValue("name""zouwei");
beanDefinition.getPropertyValues().addPropertyValue("age"30);
   // 注册BeanDefinition
registry.registerBeanDefinition("user", beanDefinition);
}
​
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
​
}
}

我们通过实现BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry()方法,可以自己构建BeanDefinitionspringUserBean的形式管理起来;下面来测试看一下:

@Autowired
private User user;
​
@GetMapping("/getUser")
public User getUser() {
return user;
}

通过在Controller写一个测试接口,我们在前端拿到的数据如下:

{
 code200,
 data: {
   name: "zouwei",
   age: 30
},
 message: "",
 error: "",
 metaInfo: null
}

我们可以看到,即使没有通过注解的方式,我们也可以通过自定义BeanDefinitionRegistryPostProcessorUser注入进来;

应用

另外,在mybatis中就很好地运用了BeanDefinitionRegistryPostProcessor的特性实现了动态扫描Mapper地功能,比如MapperScannerConfigurer它就通过实现BeanDefinitionRegistryPostProcessor接口来完成了MapperScan的功能:

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessorInitializingBeanApplicationContextAwareBeanNameAware {
     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
       if (this.processPropertyPlaceHolders) {
           this.processPropertyPlaceHolders();
      }
​
       ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
       scanner.setAddToConfig(this.addToConfig);
       scanner.setAnnotationClass(this.annotationClass);
       scanner.setMarkerInterface(this.markerInterface);
       scanner.setSqlSessionFactory(this.sqlSessionFactory);
       scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
       scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
       scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
       scanner.setResourceLoader(this.applicationContext);
       scanner.setBeanNameGenerator(this.nameGenerator);
       scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
       if (StringUtils.hasText(this.lazyInitialization)) {
           scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
      }
​
       if (StringUtils.hasText(this.defaultScope)) {
           scanner.setDefaultScope(this.defaultScope);
      }
​
       scanner.registerFilters();
       scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
  }
}

最终是通过scan()方法把basePackage里面的Mapper类全部注册到BeanDefinitionRegistry中,并最终实现了Mapper的代理和实例化。