引入链路追踪组件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;
	}
}
相关推荐
·云扬·2 分钟前
Lambda 表达式详解
java·开发语言·笔记·学习·1024程序员节
monkey_meng16 分钟前
【Rust Crate之Actix Web(一)】
开发语言·后端·rust
2401_8570262337 分钟前
SpringBoot环境下的共享汽车管理策略
spring boot·后端·汽车
星叔1 小时前
ARXML汽车可扩展标记性语言规范讲解
java·前端·汽车
2401_857622661 小时前
共享汽车管理:SpringBoot技术实现与优化
spring boot·后端·汽车
2401_857600951 小时前
SpringBoot框架:共享汽车管理的创新工具
java·spring boot·汽车
夜色呦1 小时前
SpringBoot助力的共享汽车业务优化系统
spring boot·后端·汽车
代码小鑫1 小时前
A15基于Spring Boot的宠物爱心组织管理系统的设计与实现
java·开发语言·spring boot·后端·毕业设计·宠物
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 小时前
mapper.xml 使用大于号、小于号示例
xml·java·数据库
一直学习永不止步1 小时前
LeetCode题练习与总结:迷你语法分析器--385
java·数据结构·算法·leetcode·字符串··深度优先搜索