原文链接: www.glmapper.com/2020/06/21/…
关于 BeanPostProcessor 各位一定不陌生,在 SpringBoot 源码系列-Bean 的生命周期与扩展 这篇文章中,我有介绍过 bean 的创建流程及相关扩展,就有提到 BeanPostProcessor,包括它的作用时机及如何使用,这篇文章提到的这种属于比较常规的流程,因此在绝大多数场景下,都是符合我们认知的。但是最近在排查一个问题时,发现在某些场景下,BeanPostProcessor 不生效了...
代码详见: extention-FactoryBean ; clone 之后可以直接运行 DemoApplication 即可,可以观察到 控制台不输出 GlmapperBeanPostProcessor 里面 print out 的字符串。
运行代码,即可观察到具体的执行现场;代码里除了 BeanPostProcessor 之外,另外一个是 FactoryBean,也就是本篇所要聊的重点: FactoryBean getObjectType 为 null 时导致 bean 提前初始化,从而使得作用与目标 bean 的 BeanPostProcessors 都失效了。
下面将基于这个问题,展开进行分析。
先来看下 ApplicationContext 和 bean 生命周期(仅列出部分关键流程):
从流程中可以看到:BeanPostProcessor 的注册是在 ApplicationContext 生命周期中完成的,故而当 bean 创建时,如果相应拦截器 BeanPostProcessor 还没有注册,那么其就不会起作用,这个可能有以下两种原因:
关于第一个其实很好理解,不再赘述,本篇主要基于第二个原因进行说明。
bean 被提前初始化的情况就比较多了,归纳下来都能符合同一个规律:在 创建所有 non-lazy-init bean 这一步之前,也即在创建 BeanFactoryPostProcessor 或者 BeanPostProcessor 的过程中,引发了 bean 的创建,导致其被提前初始化,大体可以分为两种情形:
对于第一种情形,比较简单,这个通常是用户的配置导致的,比如我的 TestBeanFactoryPostProcessor 中通过属性注入了目标 bean 导致了其被提前创建,最终拦截器失效(如果去掉相应 TestBeanFactoryPostProcessor 配置,可以看到拦截器是能够成功的 )。
简单代码如下,作用在 TestFacade 上的 BeanFactoryPostProcessor 可能会由于 TestFacade 的提前被创建而失效
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @@Autowired private TestFacade testFacade; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // ... } 复制代码
如何找到 bean 被提前初始化的时机呢?可以在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]) 打一个条件断点,通过 beanName 进行匹配,然后顺着 debug 堆栈往回找,就能够看到是在哪里导致了 bean 被提前创建。
对于第二种情形,其实也是通过上述方法先找到被提前创建的源头,只不过这种情形更加隐晦,也更加复杂,这里我们单独在下面的部分中来分析。
从 Spring 2.x 版本开始,BeanFactory 中就已经有 isTypeMatch 这个方法了
/** * Check whether the bean with the given name matches the specified type. * More specifically, check whether a {@link #getBean} call for the given name * would return an object that is assignable to the specified target type. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param typeToMatch the type to match against (as a {@code Class}) * @return {@code true} if the bean type matches, * {@code false} if it doesn't match or cannot be determined yet * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 2.0.1 * @see #getBean * @see #getType */ boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; 复制代码
从方法注释可以简单了解到,isTypeMatch 的作用就是:判断 JavaBean 是否匹配指定的类型。他包括两个参数:
回到案例,我们需要关注的是 isTypeMatch 和我们前面提到的**FactoryBean getObjectType 为 null 时导致 bean 提前初始化,从而使得作用与目标 bean 的 BeanPostProcessors 都失效了。**有什么关系呢?这里有两个比较关键的信息:
其实大概能够猜到的是, actoryBean getObjectType 为 null 时,导致了 当前 bean 被提前初始化,而此时 bean 的 BeanPostProcessors 还没有被注册到当前 bean ,从而导致了目标 bean 的 BeanPostProcessors 都失效。 这个也是本篇的结论,但是还是需要来看看具体原因的细节是什么样的。
我们知道,在 Spring 中,当进行 byType (除了用户主动配置 byType 注入以外,使用 @autowired 以及 @Bean 中的方法参数时等都使用的是 byType 注入) 注入时,会通过 org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>, boolean, boolean) 来寻找相应类型的 bean 。
针对 FactoryBean 而言,当判断其类型时,会先创建一个简单的(非完整的,仅仅是调用构造函数) bean ,调用其 getObjectType() ,如果发现返回为 null,那么就会再创造完整的 bean ,然后再通过 getObjectType() 获取类型进行匹配。
基于上面提到的点,结合本案例,来 debug 看下 FactoryBean typeCheck(类型检测) 机制导致的 BeanPostProcessor 不生效 的原因。
这里主要还是看下 isTypeMatch 方法执行是如何触发 bean 提前初始化的。
@Override public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { String beanName = transformedBeanName(name); // Check manually registered singletons. Object beanInstance = getSingleton(beanName, false); // 常规情况下,这里 beanInstance 是不为 null 的,但是对于提前加载的 beanInstance == null if (beanInstance != null && beanInstance.getClass() != NullBean.class) { // 判断类型是不是 FactoryBean if (beanInstance instanceof FactoryBean) { // 返回给定名称是否为工厂解除引用(以工厂解除引用前缀开始)。 &xxxx if (!BeanFactoryUtils.isFactoryDereference(name)) { // 这里拿 FactoryBean#getObjectType Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance); return (type != null && typeToMatch.isAssignableFrom(type)); } else { // 实例类型是否匹配 return typeToMatch.isInstance(beanInstance); } } // 处理泛型和代理 else if (!BeanFactoryUtils.isFactoryDereference(name)) { if (typeToMatch.isInstance(beanInstance)) { // 直接匹配暴露实例? return true; } else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) { // 泛型可能只匹配目标类,而不匹配代理… RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); Class<?> targetType = mbd.getTargetType(); if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) && typeToMatch.isAssignableFrom(targetType)) { // 还要检查原始类匹配,确保它在代理上暴露。 Class<?> classToMatch = typeToMatch.resolve(); return (classToMatch == null || classToMatch.isInstance(beanInstance)); } } } return false; } // 当前 beanName 的 bean 没有被注册过 else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) { // null instance registered return false; } // 没有找到单例实例->检查bean定义。 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // 在这个 factory 中没有找到 bean definition -> 委托 parent。 return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch); } // 检索相应的 bean 定义。 RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); Class<?> classToMatch = typeToMatch.resolve(); if (classToMatch == null) { classToMatch = FactoryBean.class; } Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ? new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch}); // Check decorated bean definition, if any: We assume it'll be easier // to determine the decorated bean's type than the proxy's type. // 检查修饰 bean definition(如果有的话):我们假设确定修饰 bean 的类型比确定代理的类型更容易。 BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) { RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd); // 预测指定bean的最终bean类型(已处理bean实例的)。由{@link #getType}和{@link #isTypeMatch}调用。不需要专门处理factorybean,因为它只应该操作原始bean类型。 // 这个实现过于简单,因为它不能处理工厂方法和实例化 awarebeanpostprocessors。对于标准bean,它只能正确地预测bean类型。要在子类中重写,应用更复杂的类型检测。 Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch); if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) { return typeToMatch.isAssignableFrom(targetClass); } } // 推断出 beanType Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch); if (beanType == null) { return false; } // 检查 bean class 是否是 FactoryBean 类型。本案例就是在这被处理到 返回 false 的 if (FactoryBean.class.isAssignableFrom(beanType)) { if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) { // 如果它是FactoryBean,我们希望看到它创建了什么(getObject),而不是工厂类。 beanType = getTypeForFactoryBean(beanName, mbd); if (beanType == null) { return false; } } } // 省略 ........ } 复制代码
这个步骤会向尝试从 FactoryBean 的 getObjectType 方法去获取类型,如果拿不到,则调用父类的进行初始化 bean 操作
// 省略 其他... if (fb != null) { // 尝试从实例的这个早期阶段获取 FactoryBean 的对象类型。这里调用的就是 FactoryBean#getObjectType 方法 Class<?> result = getTypeForFactoryBean(fb); // 本案例中这里返回的是 null, 所以会走到 else if (result != null) { return result; } else { // 这里的意思就是没有通过 FactoryBean#getObjectType 快速获取到类型 // 将执行实例当前实例,然后再获取 return super.getTypeForFactoryBean(beanName, mbd); } } // 省略 其他... 复制代码
调用父类的 getTypeForFactoryBean 方法,执行 bean 的初始化
@Nullable protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) { if (!mbd.isSingleton()) { return null; } try { // 这里开始执行 doGetBean,之前的文章里面有提到,bean 实例化的入口就是 getBean 的时候 FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true); return getTypeForFactoryBean(factoryBean); } catch (BeanCreationException ex) { // 省略日志打印部分 return null; } } 复制代码
在 doGetBean 中执行链路中,会在 initializeBean 时给当前 bean 注册 BeanPostProcessor,(applyBeanPostProcessorsBeforeInitialization 方法中) ,这里可以比较清晰的看到 BeanPostProcessor 没有作用于 目标 bean 的。
doGetBean -> createBean -> initializeBean -> applyBeanPostProcessorsBeforeInitialization
在本篇的案例中,其实比较明显的可以看到测试工程中 GlmapperFactoryBean 的 getObjectType 返回是为 null 的,也正是因为这个原因导致了 BeanPostProcessor 失效。那么如何在实际的开发过程中来规避呢?
关于第三点,前面提到过 getBeanNamesForType 的调用会触发类型检查,但其实这个方法还有些参数,参考如下:
String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit); 这里有个很重要的参数 allowEagerInit ,可以看到 spring 的注释中对其有非常详细的解释:
@param allowEagerInit whether to initialize lazy-init singletons and * objects created by FactoryBeans (or by factory methods with a * "factory-bean" reference) for the type check. Note that FactoryBeans need to be * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. 复制代码
简单来说这个参数能够控制是否允许 FactoryBean 的提前创建,如果是 false,那么也不会引发上述的 类型检测 。可以看到在 Spring 中在获取 BeanFactoryPostProcessor 以及 BeanPostProcessor 时,也都是传入 false 的。
tring[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); 复制代码
当然在一些 @Bean 的方法参数注入、@Autowire 注入等场景下,这个默认都是 true 的,无法改变;但针对平时编码过程中,如果是在比较早期的调用中,可根据情况,尽量传入 false。