13

Springの宣言型トランザクション(@Transactionalアノテーション)を「aspectj」モードで使用しています。ほとんどの場合、正常に機能しますが、機能しない場合もあります。私たちはそれを呼ぶことができますLang(それが実際に呼ばれているものだからです)。

ロードタイムウィーバーに問題を特定することができました。aop.xmlでデバッグと詳細ログをオンにすることで、織り込まれているすべてのクラスが一覧表示されます。問題のあるクラスLangは、実際にはログにまったく記載されていません。

次に、の先頭にブレークポイントを設定し、クラスがロードされるLangときにEclipseがスレッドを一時停止するようにします。Langこのブレークポイントは、LTWが他のクラスを織り込んでいるときにヒットします。したがって、ウィービングLangを試みて失敗し、それを出力しないか、または他のクラスに、Lang実際にウィービングする機会を得る前にロードを強制する参照があると推測しています。

ただし、これを小規模に再現できないため、これをデバッグし続ける方法がわかりません。続行する方法について何か提案はありますか?


更新:他の手がかりも歓迎します。たとえば、LTWは実際にどのように機能しますか?たくさんの魔法が起こっているようです。LTWからさらに多くのデバッグ出力を取得するためのオプションはありますか?私は現在持っています:

<weaver options="-XnoInline -Xreweavable -verbose -debug -showWeaveInfo">

私は前にそれについて言及するのを忘れました:春のエージェントはLTWを可能にするために使用されています、すなわちInstrumentationLoadTimeWeaver


Andy Clementの提案に基づいて、AspectJトランスフォーマーがクラスを通過したことがあるかどうかを調べることにしました。にブレークポイントを設定すると、他のクラス(JettyのWebAppClassLoaderのインスタンス)と同じクラスローダーによってロードされているにもかかわらずClassPreProcessorAgent.transform(..)、クラスがそのメソッドに到達することはないようです。Lang

次に、にブレークポイントを設定しましたInstrumentationLoadTimeWeaver$FilteringClassFileTransformer.transform(..)。その1つでさえもヒットしませんLang。また、使用しているクラスローダーに関係なく、ロードされたすべてのクラスに対してメソッドを呼び出す必要があると思います。これは次のようになり始めています:

  1. デバッグに問題があります。EclipseLangがロードしていると報告したときにロードされていない可能性があります
  2. Javaのバグ?とてつもない、しかし私はそれが起こると思います。

次の手がかり:電源を入れたところ、トランスがインストルメンテーションに追加される前に、ロードが時期尚早である-verbose:classように見えます。奇妙なことに、私のEclipseブレークポイントはこの読み込みをキャッチしません。Lang

これは、春が新たな容疑者であることを意味します。ConfigurationClassPostProcessorクラスをロードしてそれらを検査する処理が行われているようです。これは私の問題に関連している可能性があります。


の次の行ConfigurationClassBeanDefinitionReaderにより、Langクラスが読み取られます。

else if (metadata.isAnnotated(Component.class.getName()) ||
        metadata.hasAnnotatedMethods(Bean.class.getName())) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    return true;
}

特に、そのクラスのすべてのメソッドのすべてのパラメータクラスをロードするクラスをmetadata.hasAnnotatedMethods()呼び出します。getDeclaredMethods()クラスはアンロードされるはずなので、これで問題は解決しないのではないかと思います。不明な理由でJVMがクラスインスタンスをキャッシュしている可能性がありますか?

4

3 に答える 3

7

OK、問題は解決しました。基本的に、これはいくつかのカスタム拡張機能と組み合わせたSpringの問題です。誰かが似たようなものに出くわした場合、私は何が起こっているのかを段階的に説明しようとします。

まず第一に、BeanDefintionParser私たちのプロジェクトには習慣があります。このクラスの定義は次のとおりです。

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected Class<?> getBeanClass(Element element) {
        try {
            return Class.forName(element.getAttribute("class"));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class " + element.getAttribute("class") + "not found.", e);
        }
    }

// code to parse XML omitted for brevity

}

ここで、すべてのBean定義が読み取られ、BeanDefinitionRegistryPostProcessor開始された後に問題が発生します。この段階で、と呼ばれるクラスがすべてのBean定義の調査を開始し、。で注釈が付けられた、または。でメソッドを持つConfigurationClassPostProcessorBeanクラスを検索します。@Configuration@Bean

Beanのアノテーションを読み取るプロセスでは、AnnotationMetadataインターフェースを使用します。ほとんどの通常のBeanでは、と呼ばれるサブクラスAnnotationMetadataVisitorが使用されます。ただし、Bean定義を解析するときに、getBeanClass()メソッドをオーバーライドしてクラスインスタンスを返す場合は、代わりにStandardAnnotationMetadataインスタンスが使用されます。がStandardAnnotationMetadata.hasAnnotatedMethods(..)呼び出されると、が呼び出されますClass.getDeclaredMethods()。これにより、クラスローダーはそのクラスのパラメーターとして使用されるすべてのクラスをロードします。この方法でロードされたクラスは正しくアンロードされないため、AspectJトランスフォーマーが登録される前に発生するため、ウィービングされることはありません。

さて、私の問題は、私がそのようなクラスを持っていたということでした:

public class Something {
    private Lang lang;
    public void setLang(Lang lang) {
        this.lang = lang;
    }
}

Something次に、カスタムを使用して解析されたクラスのBeanがありましたControllerBeanDefinitionParser。これにより、誤ったアノテーション検出手順がトリガーされ、予期しないクラスのロードがトリガーされました。これは、AspectJがウィーブする機会を得られなかったことを意味しLangます。

解決策は、をオーバーライドするのgetBeanClass(..)ではなく、代わりにオーバーライドすることでしgetBeanClassName(..)た。これは、ドキュメントによると次のようになります。

private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected String getBeanClassName(Element element) {
        return element.getAttribute("class");
    }

// code to parse XML omitted for brevity

}

今日の教訓:getBeanClass本当に意味がない限り、上書きしないでください。実際、自分が何をしているのかを理解していない限り、独自のBeanDefinitionParserを作成しようとしないでください。

フィン。

于 2010-09-07T14:26:08.807 に答える
4

あなたのクラスが-verbose/-debug出力に記載されていない場合、それはあなたが思っているローダーによってロードされていないことを私に示唆しています。'Lang'が階層の上位にあるクラスローダーのクラスパス上にないことを100%確信できますか?ブレークポイントをトリガーした時点で、どのクラスローダーがLangをロードしていますか?

また、AspectJバージョンについては言及していません。1.6.7を使用していて、些細なaop.xml以外のltwで問題が発生した場合。1.6.8または1.6.9を使用する必要があります。

ltwは実際にどのように機能しますか?

簡単に言うと、AspectJウィーバーは、コードをウィーブする可能性のあるクラスローダーごとに作成されます。AspectJは、VMに定義される前に、クラスのバイトを変更するかどうかを尋ねられます。AspectJは、問題のクラスローダーを介して(リソースとして)「表示」できるaop.xmlファイルを調べ、それらを使用して自身を構成します。構成が完了すると、すべての包含/除外句を考慮して、指定されたとおりにアスペクトが織り込まれます。

AndyClementAspectJ
プロジェクトリード

于 2010-09-07T01:02:02.230 に答える
1

オプション1)アスペクトJはオープンソースです。それを割って開いて、何が起こっているかを見てください。

オプション2)クラスの名前をBangに変更し、機能し始めるかどうかを確認します

理由はわかりませんが、そこに「lang」をスキップするハードコーディングがあったとしても驚かないでしょう。

編集 -

ソースでこのようなコードを見る

        if (superclassnameIndex > 0) { // May be zero -> class is java.lang.Object
            superclassname = cpool.getConstantString(superclassnameIndex, Constants.CONSTANT_Class);
            superclassname = Utility.compactClassName(superclassname, false);

} else {
            superclassname = "java.lang.Object";
        }

java.lang.stuffのウィービングをスキップしようとしているようです...「lang」だけでは何も表示されませんが、そこにある可能性があります(またはバグ)

于 2010-09-01T15:28:14.993 に答える