乐趣区

关于java:排查not-eligible-for-getting-processed-by-all-BeanPostProcessors

前言

上一篇文章咱们聊了一下自定义实现的 SPI 如何与 spring 进行整合,但其实在实现的过程中有个小细节,就是原先咱们的 SPI 是带了拦截器性能,(ps:对如何实现一个带拦截器的 SPI 感兴趣的敌人,能够查看这篇文章 –> 聊聊如何实现一个带有拦截器性能的 SPI)。

为了保留这个拦截器性能,我原先的想法是狸猫换太子,在 spring 提供的后置处理器外面,把拦截器性能给整合进去,过后的实现代码如下

@Slf4j
@Deprecated
public class SpiInstancePostProcessor implements BeanPostProcessor {

    private DefaultListableBeanFactory beanFactory;

    private InterceptorHandler interceptorHandler;

    public SpiInstancePostProcessor(InterceptorHandler interceptorHandler,DefaultListableBeanFactory beanFactory) {
        this.interceptorHandler = interceptorHandler;
        this.beanFactory = beanFactory;
    }


    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if(bean.getClass().isAnnotationPresent(Activate.class)){return interceptorHandler.getInterceptorChain().pluginAll(bean);
        }
        return bean;
    }

}

性能是实现了,然而控制台却呈现如下信息

trationDelegate$BeanPostProcessorChecker : Bean ‘interceptorHandler’ of type [com.github.lybgeek.spring.interceptor.handler.InterceptorHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

排查过程

过后排查是通过管制提醒的信息,找到对应源码。在

org.springframework.context.support.PostProcessorRegistrationDelegate

找到相应的实现

@Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {if (logger.isInfoEnabled()) {logger.info("Bean'" + beanName + "'of type [" + bean.getClass().getName() +
                            "] is not eligible for getting processed by all BeanPostProcessors" +
                            "(for example: not eligible for auto-proxying)");
                }
            }
            return bean;
        }

看到这个信息,按失常做法是让

!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount

这个语句块为 false,从代码咱们很容易看出语句块为 false,有几个入口

  1. !(bean instanceof BeanPostProcessor)
  2. !isInfrastructureBean(beanName)
  3. this.beanFactory.getBeanPostProcessorCount() <
    this.beanPostProcessorTargetCount

1 和 3 看起来是没多大施展空间,咱们能够看下 2,2 的代码块如下

    private boolean isInfrastructureBean(@Nullable String beanName) {if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
                return (bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE);
            }
            return false;
        }
    }

从代码咱们能够看出

bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE

这句话是外围,然而 ROLE_INFRASTRUCTURE 这个又是什么鬼,咱们持续跟踪

    /**
     * Role hint indicating that a {@code BeanDefinition} is providing an
     * entirely background role and has no relevance to the end-user. This hint is
     * used when registering beans that are completely part of the internal workings
     * of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
     */
    int ROLE_INFRASTRUCTURE = 2;

这句话表白的意思是当申明这个角色时,这个 bean 就不是归属内部用户,而是归属 spring 外部。换句话说就是这个 bean 是个官网 bean,不是大众 bean。总之这个就是一个身份标识,因而咱们在把 bean 注入到 spring 容器中,就能够做如下解决

    @Bean
    @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
    public InterceptorHandler interceptorHandler(final ObjectProvider<List<Interceptor>> listObjectProvider) {return new InterceptorHandler(interceptorChain(listObjectProvider));
    }

当加上

 @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)

这个注解后,世界果然喧扰了,控制台再也没有呈现

not eligible for getting processed by all BeanPostProcessors

但问题真的就解决了吗?

答案见仁见智,很多时候咱们很容易坑骗一个人一时,但却很难坑骗这个人一世,好比方你通知妹子我家财万贯,妹子置信了,等到妹子要你给她买一些贵重的货色时,发现你买不起,你为了讨妹子欢心,不得以打肿脸充胖子,提前生产你压根买不起的货色,导致你前面没法再生产其余你本能够生产的货色

在 spring 世界也是如此,BeanPostProcessor 自身也是一个 Bean,一般而言其实例化机会要早过一般的 Bean,然而她当初对于你实现的 bean 有一些需要,即 BeanPostProcessor 有时也会依赖一些 Bean,这就导致了一些一般 Bean 的实例化早于 BeanPostProcessor 的可能状况, 而引发一些状况,比方这些提前初始化的 bean 无奈享有一些后置处理器扩大的性能

因而对本文的案例,用

 @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)

其实是没解决问题实质,然而因为本文的拦截器不须要后续的一些 spring 特色性能,因而这种解法也算是一种吧。

那有没有其余解法,答案是有的,咱们能够利用

org.springframework.beans.factory.SmartInitializingSingleton

这个类,他这个类外面有个

afterSingletonsInstantiated()

办法,这个办法的作用是在所有单例 bean 初始化实现调用后的回调接口。本文前面的例子就是改用这个接口实现,代码如下

public class SpiInstanceInitializingSingleton implements SmartInitializingSingleton,BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    private InterceptorHandler interceptorHandler;

    public SpiInstanceInitializingSingleton(InterceptorHandler interceptorHandler) {this.interceptorHandler = interceptorHandler;}

    @Override
    public void afterSingletonsInstantiated() {changeBeanInstance2ProxyInstance();

    }
    }

当然还能够应用 spring 的监听机制,比方监听 refresh 事件进行解决

总结

本文算是自定义实现的 SPI 如何与 spring 进行整合这篇文章的一点扩大补充

demo 链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-spring

退出移动版