web-dev-qa-db-ja.com

Springの「自動プロキシの対象外」の原因を突き止める

Springの自動プロキシ機能をいじり始めると、文書化されているようにこの動作に遭遇することがよくあります。

BeanPostProcessorインターフェースを実装するクラスは特別であるため、コンテナによって異なる方法で処理されます。すべてのBeanPostProcessorとそれらの直接参照されるBeanは、ApplicationContextの特別な起動フェーズの一部として起動時にインスタンス化され、それらすべてのBeanPostProcessorsはソートされた方法で登録され、すべてのBeanに適用されます。 AOP自動プロキシはBeanPostProcessor自体として実装されているため、BeanPostProcessorまたは直接参照されるBeanは自動プロキシに適格ではありません(したがって、「織り込まれた」側面はありません)。

このようなBeanについては、「Bean 'foo'はすべてのBeanPostProcessorsによって処理される資格がありません(たとえば、自動プロキシの資格がありません)」という情報ログメッセージが表示されます。

つまり、独自のBeanPostProcessorを記述し、そのクラスがコンテキスト内の他のBeanを直接参照する場合、それらの参照されるBeanは自動プロキシの対象にならず、その結果メッセージがログに記録されます。

私の問題は、「直接参照」が実際にはアプリケーションコンテキストのBeanの半分を取り込む推移的な依存関係のチェーンになる可能性があるため、その直接参照がどこにあるかを追跡することが非常に難しいことです。 Springが提供するのは、その単一の情報メッセージだけであり、この参照のWebでBeanがキャッチされたことを伝える以外に、それはあまり役に立ちません。

私が開発しているBeanPostProcessorには、他のBeanへの直接参照がありますが、非常に限られた参照セットです。それにもかかわらず、ログメッセージによると、コンテキスト内のほとんどすべてのBeanが自動プロキシから除外されていますが、その依存関係がどこで発生しているのかわかりません。

誰かがこれを追跡するより良い方法を見つけましたか?

43
skaffman

この質問に何らかの閉鎖をもたらすために、初期化されていないオブジェクトグラフの崩壊は、依存関係を取得するために@Autowiredを使用するBeanPostProcessorによって引き起こされ、autowireメカニズムにより他のすべてのBean定義が初期化されましたBeanPostProcessorが問題について発言する機会を得る前に。解決策は、BPPに自動配線を使用しないことです。

21
skaffman

このレシピに従ってください:

  1. IDE(これはBeanPostProcessorCheckerの内部クラスです)でAbstractApplicationContextを開きます
  2. メソッドpostProcessAfterInitializationif (logger.isInfoEnabled()) {にブレークポイントを設定します
  3. コードを実行する
  4. ブレークポイントに到達したら、スタックトレースでgetBean(String,Class<T>)の呼び出しを探します。

    これらの呼び出しの1つは、BeanPostProcessorを作成しようとします。その豆が犯人であるべきです。

背景

この状況を想像してください:

public class FooPP implements BeanPostProcessor {
    @Autowire
    private Config config;
}

configの依存関係であるため)SpringがFooPPを作成する必要がある場合、問題があります。契約では、作成されるすべてのBeanにすべてのBeanPostProcessorを適用する必要があるとされています。しかし、Springがconfigを必要とする場合、少なくとも1つのPP(つまりFooPP)があり、サービスの準備ができていません!

@Configurationクラスを使用してこのBeanを定義すると、これはさらに悪化します。

@Configuration
public class BadSpringConfig {
     @Lazy @Bean public Config config() { return new Config(); }
     @Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}

すべての構成クラスはBeanです。つまり、BadSpringConfigからBeanファクトリを構築するには、SpringはポストプロセッサfooPPを適用する必要がありますが、それを行うには、まずBeanファクトリが必要です...

この例では、循環依存関係の1つを破ることができます。 FooPPBeanFactoryAwareを実装させると、SpringがBeanFactoryをポストプロセッサに挿入できます。そうすれば、自動配線は必要ありません。

コードの後半で、Beanを遅延的に要求できます。

private LazyInit<Config> helper = new LazyInit<Config>() {

    @Override
    protected InjectionHelper computeValue() {
        return beanFactory.getBean( Config.class );
    }
};

@Override
public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
     String value = helper.get().getConfig(...);
}

LazyInitのソース

Beanファクトリとポストプロセッサの間のサイクルを中断するには、XML構成ファイルでポストプロセッサを構成する必要があります。 Springはそれを読み、混乱することなくすべての構造を構築できます。

31
Aaron Digulla

助けになるかどうかはわかりませんが、Eclipse Spring IDEgraph view は、Bean参照の整理に役立つようです。

4
Tim