spring-projects / spring-projects/spring-framework
Custom on-demand CGLIB proxies are missing reflective proxy hints
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 60.2k
- Forks
- 38.8k
- Avg merge
- 5d 2h
- Merged PRs (30d)
- 27
Description
When you generate CGLib proxy classes during the AOT phase by using the ProxyFactory, those classes will be picked up at runtime, but also need to be made available for reflection, as EnhancerFactoryData's constructor will still try to lookup CGLib generated methods at runtime via reflection, and also actually try to call those during instance creation (EnhancerFactoryData.newInstance(…)).
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class org.springsource.restbucks.drinks.Drink: Common causes of this problem include using a final class or a non-visible class
at org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:216) ~[restbucks:6.0.0-SNAPSHOT]
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:158) ~[restbucks:6.0.0-SNAPSHOT]
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110) ~[na:na]
at org.springframework.data.util.MethodInvocationRecorder.create(MethodInvocationRecorder.java:100) ~[restbucks:3.0.0-SNAPSHOT]
at org.springframework.data.util.MethodInvocationRecorder.forProxyOf(MethodInvocationRecorder.java:76) ~[restbucks:3.0.0-SNAPSHOT]
at org.springframework.data.domain.Sort$TypedSort.<init>(Sort.java:645) ~[na:na]
at org.springframework.data.domain.Sort.sort(Sort.java:143) ~[restbucks:3.0.0-SNAPSHOT]
at org.springsource.restbucks.drinks.DrinksOptions.<clinit>(DrinksOptions.java:39) ~[restbucks:na]
... 27 common frames omitted
Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.NoSuchMethodException-->org.springsource.restbucks.drinks.Drink$$SpringCGLIB$$0.CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;)
at org.springframework.cglib.proxy.Enhancer$EnhancerFactoryData.<init>(Enhancer.java:506) ~[na:na]
at org.springframework.cglib.proxy.Enhancer.wrapCachedClass(Enhancer.java:801) ~[na:na]
This can be currently worked around by also explicitly registering reflection hints for the types that proxies have been created for.
class MyRegistrar implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
var factory = new ProxyFactory();
factory.setTargetClass(Drink.class);
factory.setProxyTargetClass(true);
hints.reflection().registerType(factory.getProxyClass(classLoader), MemberCategory.INVOKE_DECLARED_METHODS);
}
}
It also looks like multiple invocations of same the ProxyFactory setup would create multiple CGLib proxy classes. So if application code accidently invokes a similar arrangement e.g. during the initialization of a static field (use case here: defining a static typed, Spring Data Sort instance via Sort.sort(MyType.class).by(MyType::getName) and the initial step requiring the creation of a proxy instance), the reflection information will be registered for the other proxy class and the arrangement will still fail at runtime, as the hints have not been registered for the type created by the statically initialized field.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with ProxyFactory and the EnhancerFactoryData reflection lookup described in the issue, then reproduce the failure using the Restbucks example and its static Sort proxy setup. Done means custom AOT-generated CGLIB proxy classes receive reflection hints automatically and repeated ProxyFactory setups do not leave the runtime proxy without the required hints.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100