ラムダ-ClassNotFoundException


8

これが私のコードの外観です。問題の匿名クラスにどのように/なぜexecutorService.submit(work::get)スローするのかは不明ClassNotFoundExceptionです。常に発生するわけではありませんが、この例外が発生すると、回復しないようです。その後の要求は同じ例外で満たされます。誰かがこれを引き起こす原因を知っていますか?

編集:私は、VMセッションでこのメソッドへのすべての呼び出しが機能するか、どれも機能しないことを確認できます-上記の例外のために一部が成功する一方で他は失敗するようなものではありません。

さらに編集:https : //bugs.openjdk.java.net/browse/JDK-8148560はまさに私が経験しているバグですが、再現性がなかったか、レポーターが応答しなかったため、このバグはクローズされました。どういうわけか、ラムダ式の結果である匿名型は、エグゼキュータが式を実行する前にガベージコレクションされているように見えますが、常にそうであるとは限りません。使用中のjdkはopenjdk1.8.0_221です。

package com.ab.cde.ct.service.impl;

@Service
public class IngestionService {
    @Autowired private TransactionTemplate transactionTemplate;
    @Autowired private AsyncTaskExecutor executorService;

    @Transactional
    public void ingest(Data data) {
        Supplier<Optional<String>> work = () -> transactionTemplate.execute(s -> {
            // actual work on the data object, enclosed in a try/catch/finally
        });
        executorService.submit(work::get); // this is where the exception gets thrown
    }
}

例外スタックトレースは次のようになります(上記のコードはプロトタイプにすぎないため、行番号は対応しません)。

2019-10-23 19:11:35,267|[http-apr-26001-exec-10]|[B6AC864143092042BBB4A0876BB51EB6.1]|[]|[ERROR] web.error.ErrorServlet  [line:142] org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoClassDefFoundError: com/ab/cde/ct/service/impl/IngestionService$$Lambda$53
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoClassDefFoundError: com/ab/cde/ct/service/impl/IngestionService$$Lambda$53
    at org.springframework.web.servlet.DispatcherServlet.triggerAfterCompletionWithError(DispatcherServlet.java:1275)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:951)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:867)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:951)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:853)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:827)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
Caused by: java.lang.NoClassDefFoundError: com/ab/cde/ct/service/impl/IngestionService$$Lambda$53
    at com.ab.cde.ct.service.impl.IngestionService$$Lambda$53/812375226.get$Lambda(Unknown Source)
    at com.ab.cde.ct.service.impl.IngestionService.ingest(IngestionService.java:264)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at com.sun.proxy.$Proxy252.ingest(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.ab.cde.ct.service.impl.IngestionService$$Lambda$53
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1364)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1185)
    ... 115 more

これは、ローカルのワークスペースまたは製品/製品前の環境で発生しますか?
Subir Kumar Sao

@SubirKumarSao非製品環境(ローカルではない)ですが、これは製品でも同様に発生する可能性があります。
mystarrocks

アノテーションを付けたメソッド@TransactionaltransactionTemplatewithin を使用する特別な理由はありますか?
ボンド

回答:


5

これは、必要なクラス(つまりTransactionCallback)を見つけることができないラムダによって生成された合成メソッドの場合であり、したがって以下のエラーが発生します。

原因:java.lang.NoClassDefFoundError:com / ab / cde / ct / service / impl / IngestionService $$ Lambda $ 53 at com.ab.cde.ct.service.impl.IngestionService $$ Lambda $ 53 / 812375226.get $ Lambda (不明なソース)

この問題を引き起こす特定のコードは

Supplier<Optional<String>> work = () -> transactionTemplate.execute(s -> {
        // actual work on the data object, enclosed in a try/catch/finally
});

これを回避するには、次のようにコードを変更します

TransactionCallback<Optional<String>> callback = transactionStatus -> {
      // your processing goes here  
      return Optional.of("some value"); 
};

Supplier<Optional<String>> work = () -> transactionTemplate.execute(callback);

上記がそれでも機能しない場合は、以下の回避策を使用してください

Object callback = (TransactionCallback<Optional<String>>)transactionStatus -> {
     // your processing goes here     
     return Optional.of("some value");
};

Supplier<Optional<String>> work = () -> transactionTemplate.execute((TransactionCallback<Optional<String>>)callback);

さらに情報が必要な場合は、コメントでお知らせください。

PS:両方が本質的に同じ目的を果たすので、@Transactionalif transactionTemplateが使用されている必要はありません。

参照:

  1. ここここでラムダ編集
  2. Javaの合成メソッド

ありがとう-私はこのリンクからできるだけ多くのことを理解しましたが、もしそうなら常に失敗するべきではありませんか?ほとんどの場合それが機能し、特定のVMセッションでのみ失敗するのはなぜですか?
mystarrocks

の場合と同様にtransactionTemplate、このメソッドには、デフォルトの@Transactional動作を使用するインタラクションと、カスタムテンプレートオブジェクトを使用するインタラクションが含まれています。簡潔にするために、すべてのコードを省略しました。
mystarrocks

おそらく言及する価値があります- TransactionCallbackこのメソッドが特定のVMセッションでそれを使用する最初のメソッドである場合、はまだ読み込まれていないようです。これは動作を説明していますか?
mystarrocks

1
正解です。ここでは、クラスロードシーケンスが重要であり、いつか失敗するのはなぜかというわけではありません。前述のソリューションでは、実行前にクラスを明示的に強制的にロードすることでこれを排除しています。
ボンド

回避策にもかかわらず、これは再び発生したため、この回答の受け入れを取り消します。エラースタックトレースから判断すると、不足しているという不満のあるクラスは、ラムダ自体から生成された匿名クラスであるように見えます。確かではありませんが、関数型インターフェイスメソッドで参照される型はありません。bugs.openjdk.java.net/browse/JDK-8148560は基本的に私が経験しているものです。
mystarrocks

0

私はこれまで、DIの問題とパッケージの解決におけるあいまいなバグ/構成の問題の両方でこれを経験しました。私はあなたの投稿から、起動が成功した後にエラーが発生し、メソッドでその行を呼び出すと正確に発生し、デバッガでヒットする可能性があると仮定しています。

最初の提案:

Gradle / Mavenで依存パッケージをチェックして、すべてに必要なバージョンがあることを確認します。また、依存関係の上位または下位バージョンを必要とする依存パッケージに影響する可能性があるバージョンをグローバルに上書きしないでください。

最初に試すべきいくつかの低ぶら下げフルーツ(それが選ぶのに十分簡単な場合):

  • JDKバージョンまたはJavaバージョンを更新します(または、チームの別の開発者が別のバージョンを使用しているかどうかを確認し、問題を再現できるかどうかを確認します)
  • Springのバージョンを更新する(マイナーバージョンも含む)
  • IDEを更新する
  • ログを追加し、問題がリリース環境で再現できるかどうかを確認します。

依存性注入については、

次のようなものを試すことをお勧めします。また、Springでの依存関係の注入には、Springにより明確な依存関係マップを提供し、アプリケーションの依存関係をデバッグする機能が向上するため、これも良い方法です。

@Service
public class IngestionService {

    private TransactionTemplate transactionTemplate;
    private AsyncTaskExecutor executorService;

    public IngestionService(TransactionTemplate transactionTemplate, AsyncTaskExecutor executorService) {
         this.transactionTemplate = transactionTemplate;
         this.executorService = executorService;
    }

    @Transactional
    public void ingest(Data data) {
        Supplier<Optional<String>> work = () -> transactionTemplate.execute(s -> {
            // actual work on the data object, enclosed in a try/catch/finally
        });
        executorService.submit(work::get); // this is where the exception gets thrown
    }
}

これをお勧めする理由はいくつかあります。

  1. Javaでは、コンストラクターが定義されていない場合、デフォルトのコンストラクターがあることが暗黙指定され、コンパイラーがコンストラクターを生成します。春になると、これは混乱を招き、パフォーマンスを低下させる可能性があります。
  2. このコンストラクターを定義すると、Springに明示的に指示します。これらの2つの依存関係に依存しています。これらの依存関係は、nullでなく、構築時に完全に解決されるBeanとしてセットアップしました。これが有効なオブジェクトになる前に、まずこれらの依存関係を初期化して渡す必要があります。
  3. これはデバッグに役立ちます。コンストラクターにブレークポイントを設定し、何が入ってくるかを検証できます。
  4. 依存関係のBean設定に問題がある場合、Springは爆発します。スプリングスタックトレースが常に最も役立つとは限りませんが、依存しているBeanを完全に分離および宣言していない問題を正しい方法でデバッグするのに役立ちます。
  5. これにより、Spring Frameworkの観点(裏で何が起こっているかわかりにくい)とアプリケーション/ドメインロジックの観点の両方から、インジェクションに関する問題の可能性を排除できます。それが後でまだnullである場合、コンストラクターで渡されたものをデバッグすることができます。つまり、nullになったか、後で割り当てが解除されたか、または2つの定義があり、スプリングが最終的に複数のexecutorServicesが作成される可能性がありますが、最初に作成されたものを渡します。

クラスが構成のコンポーネントスキャンに含まれている限り、これは有効なBean定義である必要があるため、特に各タイプのBeanが複数ある場合は、Beanを構成クラスで明示的に定義する必要があります(これも問題になる可能性があります) )

例:

@Configuration
class SomeConfiguration {

    @Bean
    public IngestionService myIngestionServiceDefaultBeanNameChangeMe(TransactionTemplate transactionTemplateParamSentBySpringAutomaticallyChangeMyName, AsyncTaskExecutor executorServiceSentBySpringAutomaticallyChangeMyName) {
         return new IngestionService(transactionTemplateParamSentBySpringAutomaticallyChangeMyName, executorServiceSentBySpringAutomaticallyChangeMyName);
    }
}

構成では、これらのBeanがこの構成または別の構成内で初期化されると、BeanメソッドのパラメーターがSpringによって自動的に送信されることに注意してください。かっこいいね

また、Beanの名前はここでのメソッド名に対応します。同じタイプの複数のBeanがあり、Springがパラメーターとして渡される可能性がある場合は、使用するBean名をSpringに指示する必要がある場合があります。これを行うには、@ Qualifierアノテーションを使用します。

これが役に立てば幸いです。少なくともインスタンス化が正しく行われていることを確認してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.