问题起因

由于服务器资源紧张,想部署一台 ElasticSearch 给所有环境(开发环境、测试环境、生产环境...)使用。

ElasticSearch 中的索引概念对标 MySQL 中的数据库概念,所以想通过动态索引,进行区分不同环境之间的数据。比如文章,开发环境使用 local-article,测试环境使用 beta-article 等等...

然后在使用 @Document 结合 SpEL 表达式动态创建索引时,报找不到 bean 👇

Could not resolve bean reference against BeanFactory

Consider defining a bean named 'xxx' in your configuration.

场景复现

使用动态索引

在实体类标记 @Document 注解,需要指定 indexName 参数,该参数可通过 SpEL 表达式进行编写。以及 createIndex 参数默认会自动创建索引。我在这里使用 #{@baseConfig.active}-article 进行编写。

INFO

SpEL 表达式中,通过 #{@xxx} or #{@'xxx'} 来引用 bean,后续如果需要引用属性,可在通过 .字段 的方式实现。

image.png

image.png

在项目启动后,看到索引确实有动态创建。

启动报错

后续笔者想到,BaseConfig 可以删除掉,因为之前自己写了一个外部依赖,也可以获取环境变量,故将 SpEL 表达式中的 @baseConfig 改成 @systemConfig

image.png

两个类差异如下:

SystemConfigBaseConfig
外部 spring.factories 文件 EnableAutoConfiguration 自动装配的 bean本工程下 ComponentScan 自动扫描包装配的 bean
bean 名为 systeonConfigbean 名为 baseConfig

后续执行启动,结果报错 👇

image.png

排查错误

笔者通过排除法进行排查错误,先从最基础的排错顺序依次排查。

是否没有装配该 bean ?

image.png

通过构造器注入 bean,调用接口 debug 排查时,确实有注入该 bean
也就意味着该 bean 是有自动装配上的。

是否类名是关键词/敏感词,导致错误?

通过修改成其他类名,也一样是报错的情况,该步骤就不细讲。

排查 bean 创建过程

image.png

通过错误日志得知,在创建最上层 ConsoleArticleController bean 时,就报错了,那么就追踪该 bean 的创建过程。

  1. Spring 会调用 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.Strin 方法,进行创建 bean

image.png

  1. 往内部接着 debug,会进入 org.springframework.beans.factory.support.AbstractBeanFactory#markBeanAsCreated 方法,bean 如果没有创建的话,会进行创建。

image.png

  1. 在这里注意到 org.springframework.beans.factory.support.AbstractBeanFactory#mergedBeanDefinitions 字段,该 Map 记录着 bean 名到具体 bean 类。

image.png

通过窥探得知:

  • keybean 的名称,value 是具体的类。
  • 引用 bean 的话,是通过 key 获取的。
  • key 的由来,如果是 ComponentScan 扫描注册的 bean,那么只有一个类名,如果是通过 EnableAutoConfiguration 自动装配的 bean 那么就是全限定类名。

结合之前的情况,baseConfig 获取的到 beansystemConfig 获取不到 bean,会不会是这个 key 的问题。

image.png

image.png

故就是 SpEL 表达式引用 bean 时,写的是类名,而不是全限定类名,导致找不到该 bean

问题解决

image.png

将该 bean 的类名改为全限定类名,后续启动验证,没有报错,并且成功创建动态索引。

image.png

image.png

结语

初期遇到该问题时困扰了一下午,一直在反复排查该 bean 是否有成功装配。后续通过深入 bean 创建过程以及结合 SpEL 表达式引用才定位到该问题的解决方案,收获颇丰。😊

Q.E.D.


好久不見,今日營業中