Solving Spring’s “not eligible for auto-proxying” Warning – 解决Spring’的 “不符合自动代理的条件 “警告

最后修改: 2021年 3月 30日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this short tutorial, we’ll see how to track down the cause of Spring’s “not eligible for auto-proxying” message and how to fix it. 

在这个简短的教程中,我们将看到如何追踪Spring的”不符合自动代理条件“消息的原因,以及如何修复它。

First, we’ll create a simple real-life code example that causes the message to appear during an application startup. Then, we’ll explain the reason why this happens.

首先,我们将创建一个简单的现实生活中的代码例子,使该消息在应用程序启动时出现。然后,我们将解释发生这种情况的原因。

Finally, we’ll present a solution to the problem by showing a working code example.

最后,我们将通过展示一个工作代码实例来介绍问题的解决方案。

2. Cause of the “not eligible for auto proxying” Message

2.造成“不符合自动代理”信息的原因

2.1. Example Configuration

2.1.配置实例

Before we explain the cause of the message, let’s build an example that causes the message to appear during the application startup.

在解释该信息的原因之前,让我们建立一个例子,使该信息在应用程序启动时出现。

First, we’ll create a custom RandomInt annotation. We’ll use it to annotate fields that should have a random integer from a specified range inserted into them:

首先,我们将创建一个自定义的RandomInt注释。我们将用它来注解那些应该从指定范围内插入一个随机整数的字段。

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

    int max();
}

Second, let’s create a DataCache class that is a simple Spring component. We want to assign to cache a random group that might be used, for example, to support sharding. To do that, we’ll annotate that field with our custom annotation:

其次,让我们创建一个DataCache类,这是一个简单的Spring组件。我们想给缓存分配一个随机组,可能会被用来支持分片。为了做到这一点,我们将用我们的自定义注解来注解该字段。

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

Now, let’s look at the RandomIntGenerator class. It’s a Spring component that we’ll use to insert random int values into fields annotated by the RandomInt annotation:

现在,让我们看一下RandomIntGenerator类。它是一个Spring组件,我们将用它来将随机的int值插入到由RandomInt注解的字段。

@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;
    }
}

It’s important to notice that we’re autowiring the DataCache class into the RandomIntGenerator via constructor injection.

需要注意的是,我们是通过constructor injection,将DataCache类自动连接到RandomIntGenerator

Finally, let’s create a RandomIntProcessor class that will be responsible for finding fields annotated with the RandomInt annotation and inserting random values into them:

最后,让我们创建一个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;
    }
}

It uses an implementation of the org.springframework.beans.factory.config.BeanPostProcessor interface to access annotated fields right before class initialization.

它使用org.springframework.beans.factory.config.BeanPostProcessor接口的实现,在类初始化之前访问注释字段。

2.2. Testing Our Example

2.2.测试我们的例子

Even though everything compiles correctly, when we run our Spring application and watch its logs, we’ll see a “not eligible for auto proxying” message generated by Spring’s BeanPostProcessorChecker class:

尽管一切都编译正确,但当我们运行Spring应用程序并观察其日志时,我们会看到由Spring的BeanPostProcessorChecker类生成的”不符合自动代理的条件“消息。

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)

What’s more, we see that our DataCache bean that depends on this mechanism has not been initialized as we intended:

更重要的是,我们看到依赖于这一机制的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());
    }
}

However, it’s worth mentioning that even though the message shows up, the application does not crash.

然而,值得一提的是,即使该信息显示出来,应用程序也没有崩溃。

2.3. Analyzing the Cause

2.3.分析原因

The warning is caused by the RandomIntProcessor class and its autowired dependencies. Classes that implement the BeanPostProcessor interface are instantiated on startup, as part of the special startup phase of the ApplicationContext, before any other beans.

该警告是由RandomIntProcessor类及其自动连接的依赖关系引起的。实现BeanPostProcessor接口的类在启动时被实例化,作为ApplicationContext的特殊启动阶段的一部分,在任何其他bean之前。

Moreover, the AOP auto-proxying mechanism is also the implementation of a BeanPostProcessor interface. As a result, neither BeanPostProcessor implementations nor the beans they reference directly are eligible for auto-proxying. What that means is that Spring’s features that use AOP, such as autowiring, security, or transactional annotations, won’t work as expected in those classes.

此外,AOP 自动代理机制也是 BeanPostProcessor 接口的实现 因此,无论是 BeanPostProcessor 实现还是它们直接引用的 Bean 都没有资格进行自动代理。这意味着Spring使用AOP的功能,如自动布线、安全或事务性注解,将无法在这些类中按预期工作。

In our case, we were able to autowire the DataCache instance into the RandomIntGenerator class without any problems. However, the group field was not populated with a random integer.

在我们的案例中,我们能够将DataCache实例自动连接到RandomIntGenerator类中,没有任何问题但是组字段并没有被填充成一个随机的整数。

3. How to Fix the Error

3.如何修复该错误

In order to get rid of the “not eligible for auto proxying” message, we need to break the cycle between the BeanPostProcessor implementation and its bean dependencies. In our case, we need to tell the IoC container to initialize the RandomIntGenerator bean lazily. We can use Spring’s Lazy annotation:

为了摆脱”不符合自动代理的条件”消息,我们需要打破BeanPostProcessor实现与其Bean依赖关系之间的循环。在我们的案例中,我们需要告诉IoC容器以懒散的方式初始化RandomIntGeneratorbean。我们可以使用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 initializes the RandomIntGenerator bean when the RandomIntProcessor requests it in the postProcessBeforeInitialization method. At that moment, Spring’s IoC container instantiates all existing beans that are also eligible for auto-proxying.

RandomIntProcessorpostProcessBeforeInitialization方法中请求时,Spring初始化了RandomIntGenerator Bean。这时,Spring的IoC容器将所有符合自动代理条件的现有Bean实例化。

In fact, if we run our application, we won’t see a “not eligible for auto proxying” message in the logs. What’s more, the DataCache bean will have a group field populated with a random integer:

事实上,如果我们运行我们的应用程序,我们不会在日志中看到”不符合自动代理的条件”消息。更重要的是,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. Conclusion

4.总结

In this article, we learned how to track down and fix the cause of Spring’s “not eligible for auto-proxying” message. Lazy initialization breaks the cycle of dependencies during bean construction.

在这篇文章中,我们学习了如何追踪并修复Spring的“不符合自动代理”消息的原因。懒惰初始化打破了Bean构建过程中的依赖性循环。

As always, the example code is available over on GitHub.

像往常一样,示例代码可在GitHub上获得