Skip to content

Custom on-demand CGLIB proxies are missing reflective proxy hints #29309

Open
@odrotbohm

Description

@odrotbohm

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: coreIssues in core modules (aop, beans, core, context, expression)theme: aotAn issue related to Ahead-of-time processingtype: enhancementA general enhancement

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions