引入链路追踪组件sleuth后的一个踩坑

最近在公司项目中引入链路追踪组件sleuth后,启动项目报BeanNotOfRequiredTypeException的异常,看报错信息,是说关于线程池bean ThreadPoolExecutor和TraceableExecutorService的类型冲突问题,项目中定义了一个ThreadPoolExecutor的bean,如下:

scss 复制代码
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolExecutorProperties properties) {	
	return new ThreadPoolExecutor(properties.getCorePoolSize(),
			properties.getMaximumPoolSize(),
			properties.getKeepAliveTime(),
			properties.getTimeUnit(),
			new ArrayBlockingQueue<>(properties.getCapacity()),
			new ThreadPoolExecutor.AbortPolicy());
}

在项目其他地方注入了这个线程池bean,按合情合理,不应该报错的, 决定跟踪下源码看到底是什么原因会导致。

首先跟踪threadPoolExecutor的bean创建,在bean创建后的初始化initializeBean部分:

less 复制代码
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	//........省略部分代码

	Object wrappedBean = bean;
	//调用前置处理器
	wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

	//初始化
	invokeInitMethods(beanName, wrappedBean, mbd);
	
	//调用后置处理器
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

	return wrappedBean;
}

在后置处理器中有一个ExecutorBeanPostProcessor处理器,跟踪进去:

typescript 复制代码
public class ExecutorBeanPostProcessor implements BeanPostProcessor {

	//.........省略部分代码

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (!ExecutorInstrumentor.isApplicableForInstrumentation(bean)) {
			return bean;
		}
		//调用ExecutorInstrumentor.instrument方法
		return new ExecutorInstrumentor(() -> sleuthAsyncProperties().getIgnoredBeans(), this.beanFactory)
				.instrument(bean, beanName);
	}
}

public Object ExecutorInstrumentor.instrument(Object bean, String beanName) {
	//.......省略部分代码
	if (bean instanceof ExecutorService) {
		if (isProxyNeeded(beanName)) {
			return wrapExecutorService(bean, beanName);
		}		
	}
	//.......省略部分代码
	return bean;
}

boolean isProxyNeeded(String beanName) {
	//判断忽略的bean是否包含当前创建的bean
	return !this.ignoredBeans.get().contains(beanName);
}

private Object wrapExecutorService(Object bean, String beanName) {
	ExecutorService executor = (ExecutorService) bean;
	boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers());
	boolean methodFinal = anyFinalMethods(executor);
	boolean cglibProxy = !classFinal && !methodFinal;
	//封装线程池bean
	return createExecutorServiceProxy(bean, cglibProxy, executor, beanName);
}

Object createExecutorServiceProxy(Object bean, boolean cglibProxy, ExecutorService executor, String beanName) {
	return getProxiedObject(bean, beanName, cglibProxy, executor, () -> {
		if (executor instanceof ScheduledExecutorService) {
			return TraceableScheduledExecutorService.wrap(this.beanFactory, executor, beanName);
		}
		//封装成TraceableExecutorService
		return TraceableExecutorService.wrap(this.beanFactory, executor, beanName);
	});
}

可以看到在后置处理器中针对ExecutorService的子类单独做了处理,如果当前创建的bean在忽略的bean里面,则不做任何封装直接返回,否则封装成TraceableExecutorService对象。也就是说我们保存在Spring容器里面的线程池对象被封装成TraceableExecutorService对象存储了。

最后在Spring的中会判断类型是否匹配,如果不能匹配就抛出异常BeanNotOfRequiredTypeException。

typescript 复制代码
if (!ClassUtils.isAssignableValue(type, result)) {
	throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}

至此报错原因是找到了,解决办法也很简单,就是把这个线程池的bean加入到忽略bean中,配置如下:

yaml 复制代码
spring:
  sleuth:
    async:
      ignoredBeans:
        - threadPoolExecutor

配置是通过配置文件SleuthAsyncProperties加载的:

typescript 复制代码
@ConfigurationProperties(prefix = "spring.sleuth.async")
public class SleuthAsyncProperties {
	private boolean enabled;

	public boolean isEnabled() {
		return this.enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
	
	private List<String> ignoredBeans = Collections.emptyList();

	public List<String> getIgnoredBeans() {
		return this.ignoredBeans;
	}

	public void setIgnoredBeans(List<String> ignoredBeans) {
		this.ignoredBeans = ignoredBeans;
	}
}
相关推荐
神奇小汤圆8 分钟前
MySQL的10种高级SQL,性能飞升
后端
AI探索者10 分钟前
LangGraph 人工干预:Human-in-the-loop 机制详解
后端
神奇小汤圆11 分钟前
Java并发核心:你以为AQS很复杂?无非是"两个队列"和"一个状态"
后端
shark_chili14 分钟前
Spring AI Alibaba 入门与实战:一文构建智能天气查询助手
后端
Java编程爱好者17 分钟前
Java 高频面试题总结(2026通用版)
后端
Java水解25 分钟前
Spring Boot 视图层与模板引擎
spring boot·后端
重庆穿山甲27 分钟前
Java开发者的大模型入门:Spring AI Alibaba组件全攻略(二)
前端·后端
Java水解32 分钟前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
重庆穿山甲33 分钟前
Java开发者的大模型入门:Spring AI Alibaba组件全攻略(一)
前端·后端
Java编程爱好者40 分钟前
小米二面:std::map和std::unordered_map谁更快?别只知道哈希表
后端