spring-projects / spring-projects/spring-framework

Custom on-demand CGLIB proxies are missing reflective proxy hints

Open
#29,309 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: core theme: aot type: enhancement
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.