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工作的大致流程图,其中标蓝色的部分为今天要讲的重点。
首先我们来看入口类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):
那么我们就可以修改他的全限定类名,scope,是否懒加载,所依赖的类名等…
当然具体需要更多的信息,可以看具体BeanDefinition的实现类。