最近在公司项目中引入链路追踪组件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;
}
}