for example: not eligible for auto-proxying问题排查

问题起因

最近项目上搭了一个demo,整合了spring-boot-admin-starter-client但是奇怪的是,服务起来之后在SpringBootAdmin中可以看到demo服务,但是无法监控到demo服务的jvm信息
查看源码后发现jvm相关的监控信息是由SimpleMeterRegistry这个Bean处理的,再查看日志,在应用服务的日志中我们可以找到如下信息

Bean 'simpleMeterRegistry' of type [io.micrometer.core.instrument.simple.SimpleMeterRegistry] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

问题探究

那么for example: not eligible for auto-proxying到底是个什么问题呢?

简单来说就是Bean实例化早了

这个问题就要从Spring的启动流程说起了。在Spring实例化Bean之前会先实例化所有的BeanPostProcessor
在这里插入图片描述

BeanPostProcessor是Bean的前后置处理器

正常来说,所有的BeanPostProcessor都应该在所有的Bean实例化前实例化好,但是,BeanPostProcessor本身也是一个Bean。既然是Bean那就可能会出现依赖其他Bean的可能。基于前面的理论,我们做如下猜想:

  1. 假如BeanPostProcessorA依赖了Component1
  2. 同时BeanPostProcessorB会在postProcessAfterInitialization中处理Component1
  3. 但是BeanPostProcessorABeanPostProcessorB先加载
  4. 那么,BeanPostProcessorA实例化时,间接实例化了Component1,可是此时BeanPostProcessorB并没有实例化,就会导致Component1过早实例化,从而错过了某些BeanPostProcessor。

当Spring发现这种问题时,就会抛出这样的日志:

Bean 'xxx' of type [xxxxxx] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

验证

理论说完,我们来验证一下:

MyComponent

@Component
public class MyComponent {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

MyPriorityBeanPostProcess

@Component
public class MyPriorityBeanPostProcess implements BeanPostProcessor, Ordered {

    @Autowired
    private MyComponent myComponent;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

MyBeanPostProcess

@Component
public class MyBeanPostProcess implements BeanPostProcessor, Ordered {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyComponent) {
            ((MyComponent) bean).setName("zhangsan");
        }
        return bean;
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

逻辑关系

  1. MyPriorityBeanPostProcess依赖了MyComponent
  2. MyBeanPostProcess对MyComponent进行了功能增强
  3. MyPriorityBeanPostProcess优先于MyBeanPostProcess加载

TestMain

public class TestMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example.spring.problem3");
        MyComponent myComponent = applicationContext.getBean(MyComponent.class);
        System.out.println("myComponent.getName() = " + myComponent.getName());
    }
}

日志

从日志中可以看出来,由于MyComponent提前实例化,导致MyBeanPostProcess对MyComponent的增强并没有生效

18:19:15.640 [main] INFO org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'myComponent' of type [com.example.spring.problem3.MyComponent] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
myComponent.getName() = null

解决方案

  1. 如果引发这个问题的源码在自己的项目中的话,那只要调整多个BeanPostProcessor的顺序就可以了。
  2. 如果源码不在自己的项目中,那只能像下面这样,手动再跑一次没有跑的BeanPostProcessor了。下面这段代码可以用来解决整合了spring-boot-admin-starter-client之后在SpringBootAdmin中看不到被监控应用的jvm信息。
    @Bean
    InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor, SimpleMeterRegistry registry) {
        return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, "");
    }
    
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值