BeanDefinitionRegistryPostProcessor如何动态注册Bean到Spring
作者:chun_soft
1、理论
一般如果想将类注册到spring容器,让spring来完成实例化,常用方式如下:
- xml中通过bean节点来配置;
- 使用@Service、@Controller、@Conponent等注解。
最近在研究通过Spring初始化时扫描自定义注解,查到了通过实现BeanDefinitionRegistryPostProcessor获取Bean,从而获得自定义注解。
Spring支持我们通过代码来将指定的类注册到spring容器中。
Spring容器初始化时,从资源中读取到bean的相关定义后,保存在BeanDefinitionMap,在实例化bean的操作就是依据这些bean的定义来做的,而在实例化之前,Spring允许我们通过自定义扩展来改变bean的定义,定义一旦变了,后面的实例也就变了,而beanFactory后置处理器,即BeanFactoryPostProcessor就是用来改变bean定义的。
通过invokeBeanFactoryPostProcessors方法用来找出所有beanFactory后置处理器,并且调用这些处理器来改变bean的定义。
BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor的实现类在其postProcessBeanFactory方法被调用时,可以对bean的定义进行控制,因此BeanDefinitionRegistryPostProcessor的实现类一共要实现以下两个方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
该方法的实现中,主要用来对bean定义做一些改变。
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException
该方法用来注册更多的bean到spring容器中,详细观察入参BeanDefinitionRegistry接口,看看这个参数能带给我们什么能力。
从BeanDefinitionRegistry可以看到,BeanDefinitionRegistry提供了丰富的方法来操作BeanDefinition,判断、注册、移除等方法都准备好了,我们在编写postProcessBeanDefinitionRegistry方法的内容时,就能直接使用入参registry的这些方法来完成判断和注册、移除等操作。
org.springframework.context.support.AbstractApplicationContext#refresh中的invokeBeanFactoryPostProcessors(beanFactory);
用来找出所有beanFactory后置处理器,并且调用这些处理器来改变bean的定义。
invokeBeanFactoryPostProcessors(beanFactory)实际上是委托
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
方法处理的。
首先处理BeanFactoryPostProcessor中的内容:
所有实现了BeanDefinitionRegistryPostProcessor接口的bean,其postProcessBeanDefinitionRegistry方法都会调用,然后再调用其postProcessBeanFactory方法,这样一来,我们如果自定义了BeanDefinitionRegistryPostProcessor接口的实现类,那么我们开发的postProcessBeanDefinitionRegistry和postProcessBeanFactory方法都会被执行一次;
boolean reiterate = true; while (reiterate) { reiterate = false; //查出所有实现了BeanDefinitionRegistryPostProcessor接口的bean名称 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { //前面的逻辑中,已经对实现了PriorityOrdered和Ordered的bean都处理过了,因此通过processedBeans过滤,processedBeans中没有的才会在此处理 if (!processedBeans.contains(ppName)) { //根据名称和类型获取bean BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class); //把已经调用过postProcessBeanDefinitionRegistry方法的bean全部放在registryPostProcessors中 registryPostProcessors.add(pp); //把已经调用过postProcessBeanDefinitionRegistry方法的bean的名称全部放在processedBeans中 processedBeans.add(ppName); //执行此bean的postProcessBeanDefinitionRegistry方法 pp.postProcessBeanDefinitionRegistry(registry); //改变退出while的条件 reiterate = true; } } } /registryPostProcessors中保存了所有执行过postProcessBeanDefinitionRegistry方法的bean, //现在再来执行这些bean的postProcessBeanFactory方法 invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory); //regularPostProcessors中保存的是所有入参中带来的BeanFactoryPostProcessor实现类,并且这里面已经剔除了BeanDefinitionRegistryPostProcessor的实现类,现在要让这些bean执行postProcessBeanFactory方法
2、实战代码
public class AnnotationScannerConfigurer implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { // 创建一个bean的定义类的对象 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestServiceImpl.class); // 将Bean 的定义注册到Spring环境 beanDefinitionRegistry.registerBeanDefinition("testService", rootBeanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { // bean的名字为key, bean的实例为value Map<String, Object> beanMap = configurableListableBeanFactory.getBeansWithAnnotation(AutoDiscoverClass.class); }
其实在实际使用过程中,Spring启动时扫描自定义注解,是通过BeanFactoryPostProcessor接口的postProcessBeanFactory方法
configurableListableBeanFactory.getBeansWithAnnotation(AutoDiscoverClass.class);
获取每一个有自定义注解的Bean。
这种方法没满足我的实际需求。
总结下
BeanFactoryPostProcessor可以修改各个注册的Bean,BeanDefinitionRegistryPostProcessor可以动态将Bean注册。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。