Spring扩展——BeanFactoryPostProcessor(BFPP)

引言

在Spring中BeanFactoryPostProcessor(后面使用简写BFPP),作为容器启动过程的对容器进行修改操作的Bean对象,是Spring框架对外提供的核心扩展点之一,Spring IoC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据),并可以修改它。同时可以定义多个

BeanFactoryPostProcessor源码

BeanFactoryPostProcessor源码如下

public interface BeanFactoryPostProcessor {
	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * 在标准初始化之后,修改应用程序上下文的内部Bean工厂。所有bean定义都将被加载,但尚未实例化任何bean。
	 * 这甚至可以覆盖或添加属性,甚至可以用于初始化bean。
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

通过源码中的注释说明,我们可以知道的是,BFPP是用于修改BeanFactory的,提供了一个回调方法,将BeanFactory作为参数,传给实现类,所以在获取到Bean工厂后,我们可以用它做IOC容器的任何操作。
BFPP有一个重要的子接口————BeanDefinitionRegistryPostProcessor(后文简写BDRPP)

BeanDefinitionRegistryPostProcessor(BDRPP)

BDRPP接口源码如下

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	/**
	 * Modify the application context's internal bean definition registry after its
	 * standard initialization. All regular bean definitions will have been loaded,
	 * but no beans will have been instantiated yet. This allows for adding further
	 * bean definitions before the next post-processing phase kicks in.
	 * 修改应用程序上下文的内部bean定义注册表。所有常规bean定义都将被加载,
	 * 但尚未实例化任何bean。这允许在下一个后处理阶段开始之前添加更多的bean定义。
	 * @param registry the bean definition registry used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

这个接口提供了一个方法,用于将registry注册器传递给实现类,实现类可以用它向BeanDefinitionsMap中添加更多的Bean定义信息,这个地方只是添加Bean定义信息,并不是要向Bean容器注册一个实例Bean。比如ConfigurationClassPostProcessor的处理(Spring源码——ConfigurationClassPostProcessor类),用于发现更多的Bean,@Configuration注解可以将类里面@Bean注解标注了方法的Bean定义加载进来,这个我们会在另一篇中详细的讲解。
BeanFactoryPostProcessor,可以通过设置Ordered和其子接口PriorityOrdered来确定各个BeanFactoryPostProcessor执行顺序。

BFPP的执行时间

我们先来看一下Spring容器启动的核心方法,位于AbstractApplicationContext中的refresh()方法

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			/**
			 * 前戏,做容器刷新前的准备工作
			 * 1、设置容器的启动时间
			 * 2、设置活跃状态为true
			 * 3、设置关闭状态为false
			 * 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
			 * 5、准备监听器和事件的集合对象,默认为空的集合
			 */
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory.
			// 创建容器对象:DefaultListableBeanFactory
			// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// Prepare the bean factory for use in this context.
			// beanFactory的准备工作,对各种属性进行填充
			prepareBeanFactory(beanFactory);
			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context.
				// 调用各种beanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);
				// Register bean processors that intercept bean creation.
				// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
				registerBeanPostProcessors(beanFactory);
				// Initialize message source for this context.
				// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲
				initMessageSource();
				// Initialize event multicaster for this context.
				// 初始化事件监听多路广播器
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				// 留给子类来初始化其他的bean
				onRefresh();
				// Check for listener beans and register them.
				// 在所有注册的bean中查找listener bean,注册到消息广播器中
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				// 初始化剩下的单实例(非懒加载的)
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
				finishRefresh();
			}
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
				destroyBeans();
				// Reset 'active' flag.
				// 重置active标志
				cancelRefresh(ex);
				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

这是Spring容器在启动过程中,经历的13个启动方法,其中invokeBeanFactoryPostProcessors(beanFactory)

BFPP的具体调用执行

用于遍历执行Spring容器中的所有实现了BFPP接口的相关方法

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		// 获取到当前应用程序上下文的beanFactoryPostProcessors变量的值,并且实例化调用执行所有已经注册的beanFactoryPostProcessor
		// 默认情况下,通过getBeanFactoryPostProcessors()来获取已经注册的BFPP,但是默认是空的,那么问题来了,如果你想扩展,怎么进行扩展工作?
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())方法中
里面内容很复杂,源码很长,所以就不粘上来了,大概思路如下

  1. 执行Spring框架添加的内部BDRPP,,它会通过入参集合的方式直接传递到具体执行方法中,比如ConfigurationClassPostProcessor(属于BDRPP)的postProcessBeanDefinitionRegistry方法
  2. 通过第1步的执行后,有可能会在容器BeanDefinitions中引入了一些新的自定义的BDRPP,还有一些原来解析XML文件时加入的BDRPP Bean,所以又执行自定义BDRPP的postProcessBeanDefinitionRegistry方法。(执行的时候 根据PriorityOrdered Ordered NoOrdered 的顺序依次执行)
  3. 通过上一步的执行后,还是有可能会引入新的BDRPP,根据PriorityOrdered Ordered NoOrdered 的顺序依次执行 postProcessBeanDefinitionRegistry方法。
  4. 循环第3步,直到不会产生新的BDRPP。
  5. 遍历执行容器中所有BFPP里的 postProcessBeanFactory方法,根据PriorityOrdered Ordered NoOrdered 的顺序依次执行

BFPP扩展

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //直接动态地向容器 一级缓存 中添加Bean实例
        beanFactory.registerSingleton("xxxx", new Student("yanchuanbin"));
    }
}

在获取到BeanFactory后,可以直接向一级缓存中加入Bean对象实例。

BDRPP扩展

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    /**
     * BeanFactoryPostProcessor 的接口
     *
     * @param beanFactory the bean factory used by the application context
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //直接添加到一级缓存
        beanFactory.registerSingleton("student", new Student("ycb"));
    }


    /**
     * BeanDefinitionRegistryPostProcessor 在BeanFactoryPostProcessor基础上 添加了一个接口
     *
     * @param registry the bean definition registry used by the application context
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //添加Bean定义,但未将Bean实例化
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
                .genericBeanDefinition(Student.class)
                .addPropertyValue("name", "yanchuanbin");
        registry.registerBeanDefinition("xxxstudent", beanDefinitionBuilder.getBeanDefinition());
    }
}

通registry向容器BeanDefinitionMap中添加Bean定义信息,跟上面的BFPP扩展有区别。

posted @ 2021-04-27 12:28  心若向阳花自开  阅读(791)  评论(0编辑  收藏  举报