Spring扩展点之BeanFactoryPostProcessor:彻底搞懂原理以及使用场景【源码分析】

spring框架流行了这么多年,地位至今不被动摇,除了spring全家桶的生态之外,它给开发者的提供的扩展也是值得一提的。那么今天就来聊聊Spring扩展点之BeanFactoryPostProcessor。

这个扩展点是发生在bean实例化之前,BeanDefinition读取完之后。所以我们在这里可以获取到BeanDefinition,以改变他默认的实例化方式。

由于文章内容是基于源码角度来讲的,考虑到有些朋友不需要看的如此的深入,所以在此贴出其简单的使用方式:加上@Component注解,使其加入到spring容器中,他会自动识别并在适当位置调用。

@Component
public class BeanFactoryPostProcessorExtension implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		// 获取指定的beanDefinition,这里操作bean定义的属性是最合适不过的
		BeanDefinition personDefinition = beanFactory.getBeanDefinition("person");
		System.out.println(personDefinition);
		// 这里建议不要调用getBean来获取某个类的实例进行一些操作,因为这里get的话,他可能还没有进行属性赋值
	}
}

后面就进行深度分析了,不感兴趣的朋友可以直接跳过,有上面的使用方式已满足日常开发需求。

首先来贴一张图:
Spring扩展点之BeanFactoryPostProcessor:彻底搞懂原理以及使用场景【源码分析】_第1张图片
这是spring工作的大致流程图,其中标蓝色的部分为今天要讲的重点。

首先我们来看入口类AnnotationConfigApplicationContext的构造函数:

	/**
	 * 创建一个新的AnnotationConfigApplicationContext,从给定的组件类派生bean定义并自动刷新上下文。
	 *
	 * @param componentClasses 一个或者多个组件
	 * {@link Configuration @Configuration} classes
	 */
	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		// 调用空参构造函数,初始化对应的解析器:注解bean定义解读器和类路径下bean定义扫描器
		this();
		/**
		 * 注册组件,主要工作就是解析注解生成bean定义对象{@link org.springframework.beans.factory.config.BeanDefinition},
		 * ban定义的子类是:{@link AnnotatedGenericBeanDefinition}
		 * 加入的其map中{@link org.springframework.beans.factory.support.DefaultListableBeanFactory.beanDefinitionMap}
		 */
		register(componentClasses);

		// 刷新上下文,调用父类的refresh核心方法
		refresh();
	}

调用this():也就是另一个构造函数,主要是初始化扫描器,用来扫描bean的。
register() :主要是注册bean,通过扫描器扫描出bean的一些信息,然封装成BeanDefinition对象,存放在DefaultListableBeanFactory.beanDefinitionMap中,其中key为bean的名称。
refresh():刷新上下文。

接下来我们看refresh()方法:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		// 同步锁,防止出现线程安全问题
		// 此方法很多地方都对线程安全做了动作
		// 专门new了一个Object对象来当做锁
		synchronized (this.startupShutdownMonitor) {
			// 准备刷新此上下文
			/**
			 * 1、设置刷新开始时间
			 * 2、分别设置closed和active(这两个是atomic并发包下面的,防止并发修改值错乱),
			 * 3、调用用户自定义的初始化property资源
			 * 4、验证比传属性:ConfigurablePropertyResolver接口中可以设置必传属性.
			 * 如果我们需要设置比传属性的话,可以监听SpringApplicationRunListeners.
			 * 并且覆盖他的environmentPrepared方法,此方法传入ConfigurableEnvironment 作为参数,然后对其进行设置
			 * 5、重置本地的程序侦听器
			 */
			prepareRefresh();

			// 告诉子类刷新内部bean工厂。
			// obtain:获得
			// 获取刷新的bean工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 准备BeanFactory以供在此上下文中使用
			prepareBeanFactory(beanFactory);

			try {
				/**
				 * 允许在上下文子类中对bean工厂进行后处理。
				 * 这里如果只是spring单独的项目的话,spring是无需对其处理的,
				 * 这里提供了一个对bean工厂进行的一个扩展点
				 * 如果需要扩展,那么就可以覆盖
				 * {@link AbstractApplicationContext#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)}
				 * 方法,向其加入一些自定义的BeanFactory后置处理器
				 */
				postProcessBeanFactory(beanFactory);

				/**
				 * 在上下文中调用注册为bean的工厂处理器。
				 * invoke:调用
				 * 调用BeanFactory注册的后置处理器
				 */
				invokeBeanFactoryPostProcessors(beanFactory);

				System.out.println("======执行完了BeanFactory的后置处理器方法======");

				/**
				 * 注册bean的后置处理器
				 * 注意:这里的思路和beanFactory的后置处理器思想一样,
				 * {@link BeanPostProcessor} :bean后置处理器的接口
				 */
				registerBeanPostProcessors(beanFactory);

				// 初始化此上下文的消息源。
				initMessageSource();

				// 为此上下文初始化事件多宿主。
				initApplicationEventMulticaster();

				// 初始化特定上下文子类中的其他特殊bean。
				onRefresh();

				// 检查侦听器bean并注册它们。
				registerListeners();

				// 完成一些没有被实例化的bean, 并且创建其实例和属性填充
				finishBeanFactoryInitialization(beanFactory);

				// 最后一步:发布相应的事件。
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// 销毁已经创建的单实例以避免悬空资源。
				destroyBeans();

				// 重置“活动”标志。
				cancelRefresh(ex);

				// 将异常传播到调用方。
				throw ex;
			}

			finally {
				// 重置Spring核心中的公共内省缓存,因为我们可能不再需要单例bean的元数据了。。。
				resetCommonCaches();
			}
		}
	}

其中invokeBeanFactoryPostProcessors就是执行我们自定义的beanFactory后置处理器的方法了,在此方法的前面,可以看到,前面还没有做啥事情,可以看出,这里的扩展还是比较早的扩展。
然后继续看invokeBeanFactoryPostProcessors的方法体:抛开了不重要的

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		// 这里获取所有注册进入的beanFactory,然后调用其后处理器方法
		PostProcessorRegistrationDelegate
				.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
		// ...
	}

可以看到他将执行BeanFactory后置处理器方法的工作委派给了PostProcessorRegistrationDelegate。
然后调用getBeanFactoryPostProcessors获取所有的后置处理器,

	public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
		return this.beanFactoryPostProcessors;
	}

由于这里面的后置处理器是AbstractApplicationContext#beanFactoryPostProcessors的一个属性,这里可以通过编程的方式向其添加后置处理器,而我们自己配置的那种是加入到spring容器中的,所以这里返回的并不包含我们自定义的后置处理器。

然后继续看委派的执行器里面的方法体:

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		// 根据:BeanFactoryPostProcessor类型,从所有bean定义中获取类名,因为bean定义已经扫描过了,所以这里能够拿到
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class,
						true, false);
		for (String ppName : postProcessorNames) {
			// 如果已经存在了,那么这里没有做任何事情,就不添加进去额
			if (processedBeans.contains(ppName)) {
				// 跳过——已经在上面的第一阶段处理
			}
			else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				// 如果存在PriorityOrdered,它的优先级是最高的
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				// 如果指定了顺序的
				orderedPostProcessorNames.add(ppName);
			}
			else {
				// 如果没有指定顺序的
				nonOrderedPostProcessorNames.add(ppName);
			}
		}
	}
	// 一波排序之后... 
	invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
}

从这里就可以看到,我们只要实现了BeanFactoryPostProcessor接口,并且将其实现类加入到spring容器当中,他就会在这个位置来解析我们自定义的BeanFactory,并且下面会执行方法。
spring很多的扩展都是通过这样的方式来提供给我们开发者的。

循环调用其方法:

	private static void invokeBeanFactoryPostProcessors(
			Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
		// 实际调用的在这里。
		for (BeanFactoryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanFactory(beanFactory);
		}
	}

到此其原理已经大致讲完,那么这个扩展点spring提供出来给我们干什么的呢,有什么用呢?
是否ConfigurableListableBeanFactory的api我们都可以使用呢?
接下来我们看看BeanFactoryPostProcessor上面的注释:

import org.springframework.beans.BeansException;

/**
 * 工厂钩子:也可以叫做工厂的回调,
 * 允许自定义修改应用程序上下文的bean定义,调整上下文底层bean工厂的bean属性值。
 *
 * 对于覆盖在应用程序上下文中配置的bean属性的系统管理员的自定义配置文件非常有用。
 *
 * 请参阅{@link PropertyResourceConfigurer}及其具体实现,以了解解决这种配置需求的开箱即用解决方案。
 * PropertyResourceConfigurer:也是一个工厂的后置处理器,
 *
 * 一个{@code BeanFactoryPostProcessor}可以与bean定义交互并修改bean定义,
 * 但从不与bean实例交互。这样做可能会导致过早的bean实例化,破坏容器并导致意外的副作用。{@link BeanPostProcessor}。
 * 这里说可以与bean的定义进行交互,但是不要和bean的实例进行交互,如果要和bean的实例进行交互,
 * 建议使用BeanPostProcessor扩展点
 *
 * 注册:
 * {@code ApplicationContext}自动检测其bean定义中的{@code BeanFactoryPostProcessor} bean,
 * 并在创建任何其他bean之前应用它们。一个{@code BeanFactoryPostProcessor}.
 * 也可以通过程序注册一个{@code ConfigurableApplicationContext}。
 * 这里讲spring应用会自动检测BeanFactoryPostProcessor,并且在创建bean之前应用他们,
 * 同时也可以通过程序来注册他们。
 *
 * 排序:
 * 1、自定检测到的bean会按照ordered和PriorityOrdered进行排序
 * 2、对于编程的方式注册的后置处理器,他会按照注册的顺序进行排序,
 * 如果实现了order接口,也会被忽略:底层是一个list,编程的方式就是add
 *
 */
@FunctionalInterface
public interface BeanFactoryPostProcessor {

	/**
	 * BeanFactory后置处理器:
	 *
	 * 在标准初始化之后修改应用程序上下文的内部bean工厂。
	 * 所有bean定义都已加载,但还没有实例化bean。这允许覆盖或添加属性,甚至可以在快速初始化bean中。
	 * 此后置处理器所在的阶段为bean定义获取之后,bean实例化之前,这里可以对其bean工厂进行改造,
	 * 方法:{@link ConfigurableListableBeanFactory#getBeanDefinition(java.lang.String)}
	 * 可以获取指定的bean定义,返回其bean定义的对象,如果想要扩展,可以对其进行修改
	 * bean实例化就是通过bean定义的信息进行实例化的
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

这些后置处理器,他会在所有bean实例化之前进行应用,主要是提供给开发者去修改bean定义,以达到bean实例的生成按照自己的方式来生成。这里不建议调用getBean方法,使其bean过早的实例化,这可能会破坏容器并导致意外的副作用。它的执行顺序可以按照Order来指定:order数字越大,优先级越低。
比如:

@Component
@Order(1)
public class BeanFactoryPostProcessorExtension implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		// ...
	}
}

上面既然说到了能够修改bean定义,那么我们就要研究一下这个bean定义(BeanDefinition):

Spring扩展点之BeanFactoryPostProcessor:彻底搞懂原理以及使用场景【源码分析】_第2张图片
Spring扩展点之BeanFactoryPostProcessor:彻底搞懂原理以及使用场景【源码分析】_第3张图片
那么我们就可以修改他的全限定类名,scope,是否懒加载,所依赖的类名等…
当然具体需要更多的信息,可以看具体BeanDefinition的实现类。

你可能感兴趣的:(源码,Springboot,spring)