1. 概要

この短いチュートリアルでは、Springの「自動プロキシの対象外」メッセージの原因を突き止める方法と、それを修正する方法を説明します。

まず、アプリケーションの起動時にメッセージを表示する簡単な実際のコード例を作成します。 次に、これが発生する理由を説明します。

最後に、実用的なコード例を示して、問題の解決策を示します。

2. 「自動プロキシの対象外」メッセージの原因

2.1. 構成例

メッセージの原因を説明する前に、アプリケーションの起動時にメッセージが表示される例を作成しましょう。

まず、カスタムRandomIntアノテーションを作成します。 これを使用して、指定した範囲のランダムな整数を挿入する必要があるフィールドに注釈を付けます。

@Retention(RetentionPolicy.RUNTIME)
public @interface RandomInt {
    int min();

    int max();
}

次に、単純なSpringコンポーネントであるDataCacheクラスを作成しましょう。 たとえば、シャーディングをサポートするために使用される可能性のあるランダムグループをキャッシュに割り当てる必要があります。 そのために、そのフィールドにカスタムアノテーションを付けます。

@Component
public class DataCache {
    @RandomInt(min = 2, max = 10)
    private int group;
    private String name;
}

それでは、RandomIntGeneratorクラスを見てみましょう。 これはSpringコンポーネントであり、RandomIntアノテーションで注釈が付けられたフィールドにランダムなint値を挿入するために使用します。

@Component
public class RandomIntGenerator {
    private Random random = new Random();
    private DataCache dataCache;

    public RandomIntGenerator(DataCache dataCache) {
        this.dataCache = dataCache;
    }

    public int generate(int min, int max) {
        return random.nextInt(max - min) + min;
    }
}

DataCacheクラスをコンストラクタインジェクションを介してRandomIntGeneratorに自動配線していることに注意してください。

最後に、 RandomIntProcessor クラスを作成して、 RandomInt アノテーションが付けられたフィールドを検索し、それらにランダムな値を挿入します。

public class RandomIntProcessor implements BeanPostProcessor {
    private final RandomIntGenerator randomIntGenerator;

    public RandomIntProcessor(RandomIntGenerator randomIntGenerator) {
        this.randomIntGenerator = randomIntGenerator;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            RandomInt injectRandomInt = field.getAnnotation(RandomInt.class);
            if (injectRandomInt != null) {
                int min = injectRandomInt.min();
                int max = injectRandomInt.max();
                int randomValue = randomIntGenerator.generate(min, max);
                field.setAccessible(true);
                ReflectionUtils.setField(field, bean, randomValue);
            }
        }
        return bean;
    }
}

org.springframework.beans.factory.config.BeanPostProcessor インターフェースの実装を使用して、クラスの初期化の直前に注釈付きフィールドにアクセスします。

2.2. 例のテスト

すべてが正しくコンパイルされていても、Springアプリケーションを実行してそのログを監視すると、SpringのBeanPostProcessorChecker[によって生成された「自動プロキシの対象外」メッセージが表示されます。 X210X]クラス:

INFO org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'randomIntGenerator' of type [com.baeldung.autoproxying.RandomIntGenerator] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

さらに、このメカニズムに依存する DataCache beanが、意図したとおりに初期化されていないことがわかります。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, RandomIntGenerator.class})
public class NotEligibleForAutoProxyingIntegrationTest {

    private RandomIntProcessor randomIntProcessor;

    @Autowired
    private DataCache dataCache;

    @Test
    public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenNotEligibleLogShouldShow() {
        assertEquals(0, dataCache.getGroup());
    }
}

ただし、メッセージが表示されても、アプリケーションはクラッシュしないことに注意してください。

2.3. 原因の分析

警告は、RandomIntProcessorクラスとその自動配線された依存関係によって発生します。 BeanPostProcessor interface を実装するクラスは、他のBeanの前に、ApplicationContextの特別な起動フェーズの一部として起動時にインスタンス化されます。

さらに、AOP自動プロキシメカニズムは BeanPostProcessor interface の実装でもあります。その結果、BeanPostProcessorの実装もそれらが直接参照するBeanも適格ではありません。自動プロキシ用。 つまり、自動配線、セキュリティ、トランザクションアノテーションなど、AOPを使用するSpringの機能は、これらのクラスでは期待どおりに機能しません。

私たちの場合、自動配線することができました DataCache インスタンスに RandomIntGenerator 問題なくクラスでも グループフィールドにはランダムな整数が入力されていません。

3. エラーを修正する方法

自動プロキシの対象外」メッセージを取り除くには、BeanPostProcessor実装とそのBean依存関係の間のサイクルを中断する必要があります。 この場合、RandomIntGeneratorBeanを遅延初期化するようにIoCコンテナに指示する必要があります。 SpringのLazyアノテーションを使用できます。

public class RandomIntProcessor implements BeanPostProcessor {
    private final RandomIntGenerator randomIntGenerator;

    @Lazy
    public RandomIntProcessor(RandomIntGenerator randomIntGenerator) {
        this.randomIntGenerator = randomIntGenerator;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //...
    }
}

Springは、RandomIntProcessorpostProcessBeforeInitializationメソッドで要求すると、RandomIntGeneratorBeanを初期化します。 その時点で、SpringのIoCコンテナーは、自動プロキシの対象でもある既存のすべてのBeanをインスタンス化します。

実際、アプリケーションを実行すると、「自動プロキシの対象外です」メッセージがログに表示されません。 さらに、 DataCache beanには、ランダムな整数が入力されたグループフィールドがあります。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, RandomIntGenerator.class})
public class NotEligibleForAutoProxyingIntegrationTest {

    private RandomIntProcessor randomIntProcessor;

    @Autowired
    private DataCache dataCache;

    @Test
    public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenGroupFieldShouldBePopulated() {
        assertNotEquals(0, dataCache.getGroup());
    }
}

4. 結論

この記事では、Springの「自動プロキシの対象外」メッセージの原因を突き止めて修正する方法を学びました。 遅延初期化は、Bean構築中の依存関係のサイクルを中断します。

いつものように、サンプルコードはGitHubから入手できます。