前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >排查not eligible for getting processed by all BeanPostProcessors

排查not eligible for getting processed by all BeanPostProcessors

作者头像
lyb-geek
发布2022-01-07 10:09:31
发布2022-01-07 10:09:31
2.3K00
代码可运行
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路
运行总次数:0
代码可运行

01

前言

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

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

代码语言:javascript
代码运行次数:0
运行
复制
@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;
    }

}

功能是实现了,但是控制台却出现如下信息

代码语言:javascript
代码运行次数:0
运行
复制
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)

02

排查过程

当时排查是通过控制提示的信息,找到对应源码。在

代码语言:javascript
代码运行次数:0
运行
复制
org.springframework.context.support.PostProcessorRegistrationDelegate

找到相应的实现

代码语言:javascript
代码运行次数:0
运行
复制
@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;
    }

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

代码语言:javascript
代码运行次数:0
运行
复制
!(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的代码块如下

代码语言:javascript
代码运行次数:0
运行
复制
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;
    }
  }

从代码我们可以看出

代码语言:javascript
代码运行次数:0
运行
复制
bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE

这句话是核心,然而ROLE_INFRASTRUCTURE这个又是什么鬼,我们继续跟踪

代码语言:javascript
代码运行次数:0
运行
复制
/**
   * 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容器中,就可以做如下处理

代码语言:javascript
代码运行次数:0
运行
复制
@Bean
    @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
    public InterceptorHandler interceptorHandler(final ObjectProvider<List<Interceptor>> listObjectProvider) {
        return new InterceptorHandler(interceptorChain(listObjectProvider));
    }

当加上

代码语言:javascript
代码运行次数:0
运行
复制
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)

这个注解后,世界果然清净了,控制台再也没有出现

代码语言:javascript
代码运行次数:0
运行
复制
not eligible for getting processed by all BeanPostProcessors

但问题真的就解决了吗?

答案见仁见智,很多时候我们很容易欺骗一个人一时,但却很难欺骗这个人一世,好比如你告诉妹子我家财万贯,妹子相信了,等到妹子要你给她买一些贵重的东西时,发现你买不起,你为了讨妹子欢心,不得以打肿脸充胖子,提前消费你压根买不起的东西,导致你后面没法再消费其他你本可以消费的东西

在spring世界也是如此,BeanPostProcessor本身也是一个Bean,一般而言其实例化时机要早过普通的Bean,但是她现在对于你实现的bean有一些需求,即BeanPostProcessor有时也会依赖一些Bean,这就导致了一些普通Bean的实例化早于BeanPostProcessor的可能情况,而引发一些情况,比如这些提前初始化的bean无法享有一些后置处理器扩展的功能

因此对本文的案例,用

代码语言:javascript
代码运行次数:0
运行
复制
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)

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

那有没有其他解法,答案是有的,我们可以利用

代码语言:javascript
代码运行次数:0
运行
复制
org.springframework.beans.factory.SmartInitializingSingleton

这个类,他这个类里面有个

代码语言:javascript
代码运行次数:0
运行
复制
afterSingletonsInstantiated()

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

代码语言:javascript
代码运行次数:0
运行
复制
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事件进行处理

03

总结

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

04

demo链接

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linyb极客之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01
  • 02
  • 03
  • 04
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档