Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
odrotbohm opened this issue Oct 12, 2022 · 1 comment
Open

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

odrotbohm opened this issue Oct 12, 2022 · 1 comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement

Comments

@odrotbohm
Copy link
Member

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.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Oct 12, 2022
@christophstrobl
Copy link
Member

Ran into the same issue with data-mongodb lazy loading proxies. I also had to add access to declared fields and ended up with this

Class<?> proxyClass = LazyLoadingProxyFactory.resolveProxyType(field.getType(),() -> LazyLoadingInterceptor.none());
						
generationContext.getRuntimeHints().reflection().registerType(proxyClass,
	MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS);

to avoid

java.lang.IllegalStateException: Failed to execute CommandLineRunner",
    "	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:772) ~[data-mongodb:3.0.0-SNAPSHOT]",
    "	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:753) ~[data-mongodb:3.0.0-SNAPSHOT]",
    "	at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[data-mongodb:3.0.0-SNAPSHOT]",
    "	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[data-mongodb:3.0.0-SNAPSHOT]",
    "	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[data-mongodb:3.0.0-SNAPSHOT]",
    "	at com.example.data.mongodb.DataMongoDbApplication.main(DataMongoDbApplication.java:22) ~[data-mongodb:na]",
    "Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.NoSuchFieldException-->CGLIB$FACTORY_DATA",
    "	at org.springframework.cglib.proxy.Enhancer.wrapCachedClass(Enhancer.java:813) ~[na:na]",
    "	at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:111) ~[na:na]",
    "	at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) ~[na:na]",
    "	at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:55) ~[na:na]",
    "	at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[data-mongodb:na]",
    "	at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:62) ~[na:na]",
    "	at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:35) ~[na:na]",
    "	at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) ~[na:na]",
    "	at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) ~[data-mongodb:6.0.0-SNAPSHOT]",
    "	at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:567) ~[na:na]",
    "	at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:412) ~[na:na]",
    "	at org.springframework.data.mongodb.core.convert.LazyLoadingProxyFactory.getEnhancedTypeFor(LazyLoadingProxyFactory.java:141) ~[na:na]",

@sdeleuze sdeleuze added the theme: aot An issue related to Ahead-of-time processing label Nov 19, 2022
@jhoeller jhoeller changed the title CGLib proxy creation during AOT needs to register reflective proxy hints Lazy resolution proxies and other on-demand CGLIB proxies are missing reflective proxy hints Nov 26, 2022
@jhoeller jhoeller self-assigned this Nov 26, 2022
@jhoeller jhoeller added in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Nov 26, 2022
@jhoeller jhoeller changed the title Lazy resolution proxies and other on-demand CGLIB proxies are missing reflective proxy hints Custom on-demand CGLIB proxies are missing reflective proxy hints Nov 26, 2022
@jhoeller jhoeller added this to the 6.x Backlog milestone Dec 29, 2023
@jhoeller jhoeller removed their assignment Jan 9, 2024
@jhoeller jhoeller modified the milestones: 6.x Backlog, General Backlog Jun 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants