Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

NPI – Lightrun – Spring (partner)

We rely on other people’s code in our own work. Every day. It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.

The problem is, of course, when things fall apart in production - debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky. It’s difficult to understand what talks to what and, specifically, which part of the underlying library is at fault.

Lightrun is a new kind of debugger.

It's one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics. No hotfixes, redeployments, or restarts required.

Learn more in this quick, 5-minute Lightrun tutorial:

>> The Essential List of Spring Boot Annotations and Their Use Cases

1. Overview

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. 

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.1. Example Configuration

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:

@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:

@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:

@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.

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:

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.

2.2. Testing Our Example

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:

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:

@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

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.

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.

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.

3. How to Fix the Error

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:

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.

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:

@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

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.

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

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Generic footer banner
Comments are closed on this article!