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的可能。基于前面的理论,我们做如下猜想:
- 假如BeanPostProcessorA依赖了Component1
- 同时BeanPostProcessorB会在
postProcessAfterInitialization
中处理Component1。 - 但是BeanPostProcessorA比BeanPostProcessorB先加载
- 那么,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;
}
}
逻辑关系
- MyPriorityBeanPostProcess依赖了MyComponent
- MyBeanPostProcess对MyComponent进行了功能增强
- 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
解决方案
- 如果引发这个问题的源码在自己的项目中的话,那只要调整多个BeanPostProcessor的顺序就可以了。
- 如果源码不在自己的项目中,那只能像下面这样,手动再跑一次没有跑的BeanPostProcessor了。下面这段代码可以用来解决整合了
spring-boot-admin-starter-client
之后在SpringBootAdmin中看不到被监控应用的jvm信息。@Bean InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor, SimpleMeterRegistry registry) { return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, ""); }