See https://github.com/spring-projects/spring-retry/issues/214#issuecomment-857078807
Spring retry uses AOP auto proxy to add a proxy for @Retryable
methods; the code unconditionally wraps the target in a new proxy. If proxyTargetClass
is true (default in Spring Boot), the AbstractAutoProxyCreator
does not add interfaces if the target is a JDK proxy.
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
This causes, for example, @Retryable
on a spring data repository to create an invalid bean
@Repository
public interface TestRepository extends CrudRepository<Test, String> {
@Override
@Retryable(maxAttempts = 4)
List<Test> findAll();
}
The inner (spring-data) proxy implements TestRepository
, etc, but the outer proxy only implements the Retryable
interceptor interface.
The bean 'testRepository' could not be injected as a 'com.example.demo.TestRepository' because it is a JDK dynamic proxy that implements:
org.springframework.data.repository.CrudRepository
Although the error message is wrong (https://github.com/spring-projects/spring-boot/issues/26821), it is because the proxy only implements Retryable
.
Perhaps the AbstractAutoProxyCreator
could detect that the target is a JDK proxy and copy the interfaces (even when proxyTargetClass
) is true?
Comment From: jhoeller
This seems to fail only in case of an introduction advice, where our fallback check in DefaultAopProxyFactory
is misled into assuming that an interface has been configured (even if it was just an introduced interface), unfortunately skipping the fallback in that case. I've revised AbstractAutoProxyCreator
to specifically handle JDK proxy targets in such a scenario, explicitly adding their original interfaces.