引入链路追踪组件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;
	}
}
相关推荐
WalkingWithTheWind~7 分钟前
Linux搭建Nginx直播流媒体服务RTMP/RTSP转Http-flv视频浏览器在线播放/Vue/Java/ffmpeg
java·linux·nginx·ffmpeg·vue·http-flv·rtsp
FLZJ_KL12 分钟前
【设计模式】【创建型模式】抽象工厂模式(Abstract Factory)
java·设计模式·抽象工厂模式
粉03211 小时前
Keeppalived 实现Nginx 的高可用集群
java·服务器·nginx
路在脚下@1 小时前
Java使用Redisson实现布隆过滤器
java·spring boot
魔道不误砍柴功1 小时前
Java中的Stream API:从入门到实战
java·windows·python
晚安7201 小时前
idea添加web工程
java·前端·intellij-idea
xinghuitunan1 小时前
时间转换(acwing)c/c++/java/python
java·c语言·c++·python
八月五2 小时前
Java并发编程——ThreadLocal
java·并发编程
多敲代码防脱发2 小时前
Spring框架基本使用(Maven详解)
java·网络·后端·spring·maven