解決 Elasticsearch 使用 Java High Level REST Client 時出現 NoClassDefFoundError 錯誤
因為工作關係需要用到 Elasticsearch,評估過後決定使用 high-level REST client 來進行開發,但在環境建置上卻出現了一些問題
錯誤訊息如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'restHighLevelClient' defined in class path resource [org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration$RestHighLevelClientConfiguration.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.elasticsearch.client.RestHighLevelClient] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@5a2e4553]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
at (中略...)Caused by: java.lang.NoClassDefFoundError: org/elasticsearch/client/Cancellable
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3119) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2268) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:662) ~[spring-core-5.1.2.RELEASE.jar:5.1.2.RELEASE]
... 27 common frames omittedCaused by: java.lang.ClassNotFoundException: org.elasticsearch.client.Cancellable
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[na:na]
... 31 common frames omitted
原始錯誤訊息非常長所以中間截掉一些,但基本上可以看得出來是有一些 class 找不到
在網路上搜尋到一些解法,大部分都是説少了 Elasticsearch 的核心 dependency,也有些說是底層 Lucene 版本衝突,然而我的 pom.xml 已經加上核心的 dependency 了
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.5.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.5.0</version>
</dependency>
也確認過沒有另外 import Lucene 造成版本衝突,那麼還有什麼原因呢?
之後打開專案的 External Libraries 來檢查後看到有個可疑的地方
External Libraries
齁齁,看起來就是你在使壞(?
在 Elasticsearch 的官方文件中有提到:
We released a low-level REST client in 2016, which is based on the well known Apache HTTP client and it allows to communicate with an Elasticsearch cluster in any version using HTTP. On top of that we released the high-level REST client which is based on the low-level client but takes care of request marshalling and response un-marshalling.
可以知道 high-level REST client 是基於 low-level REST client 的,但在 high-level 的 dependency 頁面中卻說只需要加入核心就好,並沒有另外提到要加入 low-level 的 library:
high-level dependency
雖然實際上核心的確有幫我們載入 low-level 的 library 沒錯,但版本卻沒有對應到我 high-level 所需要的 7.5.0 版本啊!
因此我們需要做的,就是自已手動把 low-level 的client 自己加入到 pom.xml 裡面:
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.5.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.5.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.5.0</version>
</dependency>
就可以順利解決這個問題囉!
正確的版本
最後溫馨提醒一下,如果在網路上搜尋 ES 的相關實作,有很多範例都是使用 ES 的 transport client(大部分都是簡體字的文章),但這個在 7.X 版本中已經 deprecated 了,預計在 8.X 版本中會移除掉,實作前記得看清楚喔
參考資料:
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/_motivations_around_a_new_java_client.html
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.5/java-rest-high-getting-started-dependencies.html
https://stackoverflow.com/questions/46854919/noclassdeffounderror-error-creating-resthighlevelclient-bean