After upgrading from spring boot 3.5.7 to 4.0.0 I am unable to get beans from BeanFactory.

I have classes A1, A2, A3... implementing interface A. In old code implementation of A is selected at runtime dynamically based on request context using BeanFactory:

public A getA(String version) {
  return switch (version) {
    ...
   case "5" -> beanFactory.getBean(A5.class);
  }
}

This code now throws BeanNotOfRequiredTypeException because actual type is class jdk.proxy1.$Proxy....

When I add annotation @ Proxyable(TARGET_CLASS) to all my A1..An classes, everything starts working as previously.

Below fully working example with incorrect behavior with slightly different exception, but I believe this is the same root cause:

package com.example.demo;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.resilience.annotation.EnableResilientMethods;
import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Component;

@EnableResilientMethods
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    interface A {
        int getValue();
    }

    @Component
    class A1 implements A {

        @Retryable
        @Override
        public int getValue() {
            return 1;
        }
    }

    @Component
    class A2 implements A {

        @Retryable
        @Override
        public int getValue() {
            return 2;
        }
    }

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {

            try {
                BeanFactory beanFactory = ctx;
                var myA = beanFactory.getBean(A1.class);
                System.out.println(myA.getClass());
                System.out.println(myA.getValue());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        };
    }
}

After commenting out '@EnableResilientMethods' - everything works.

Comment From: rafaljaw

If I add @ Lazy to my bean then I see the error more similar to the one I saw in my original code when first time saw it but which obviously I cannot share because it is part of bigger code base I am working on:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'com.example.demo.DemoApplication$A1' is expected to be of type 'com.example.demo.DemoApplication$A1' but was actually of type 'com.example.demo.$Proxy62'
    at org.springframework.beans.factory.support.AbstractBeanFactory.adaptBeanInstance(AbstractBeanFactory.java:418)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1626)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1581)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:556)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:384)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:377)

Comment From: jhoeller

If this is caused by @EnableResilientMethods, does @EnableResilientMethods(proxyTargetClass=true) help? That forces it to not consider interface proxies by default. I wonder why this is not happening by default in Boot against the default proxy config there - in Boot, I'd expect that flag to effectively default to true.

Other than that, your use of @Proxyable is actually as intended, expressing that your bean class always needs to be proxied with its full class despite implementing a service interface.

Comment From: rafaljaw

You are right, @EnableResilientMethods(proxyTargetClass = true)also helps, thanks for pointing this. I was expecting this should work no matter what value will be used for proxyTargetClass.

Comment From: jhoeller

It looks like we got a gap with Boot's default setup since this issue has effectively been raised before in https://github.com/spring-projects/spring-framework/issues/35286#issuecomment-3323784653 - this defaulting works on the Framework side, we'll need to investigate this on the Boot side.

Comment From: jhoeller

It turns out that this does work with a direct RetryAnnotationBeanPostProcessor definition but not with @EnableResilientMethods due to a mismatch on the Framework side. To be fixed for 7.0.2.