Infer hints required for aspects
mhalbritter opened this issue · 10 comments
In the spring-native sample class-proxies-aop
in the branch sb-3.0.x
, the @Aspect
works when running in AOT mode, but not in a native-image.
There's no exception, the aspect just doesn't get executed.
The proxy is now created properly but the aspect
smoke test still fails.
I was able to make it work with:
static class AspectRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerType(TestAspect.class,
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
hints.proxies().registerJdkProxy(FactoryBean.class, BeanClassLoaderAware.class, ApplicationListener.class);
hints.proxies().registerJdkProxy(ApplicationAvailability.class, ApplicationListener.class);
}
}
Reflection hint should probably be inferred on Spring Framework side.
The 2 proxies are required by org.springframework.boot.availability.ApplicationAvailabilityBean
and org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar
and are not created automatically by my #28980 local fix (@jhoeller could you please confirm that's expected?). Related stacktrace is:
at java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:48) ~[aspect:na]
at java.lang.reflect.Proxy.getProxyClass(Proxy.java:398) ~[aspect:na]
at org.springframework.util.ClassUtils.createCompositeInterface(ClassUtils.java:784) ~[na:na]
at org.springframework.aop.aspectj.AspectJExpressionPointcut.getTargetShadowMatch(AspectJExpressionPointcut.java:437) ~[na:na]
at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:295) ~[na:na]
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:251) ~[na:na]
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:288) ~[na:na]
at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:320) ~[na:na]
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:128) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:97) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:78) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:366) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:318) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:435) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1757) ~[aspect:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[aspect:6.0.0-SNAPSHOT]
If it is confirmed those can't be inferred, I guess those proxies hints should be contributed on Boot side
Looks like the aspect
smoke test samples now work, but there are other issues preventing it to work in a reliable fashion for various use case. #29519 is likely one of the blockers.
We should also check if aspect annotations and the methods and classes where they are applied are available via reflection, I suspect #29765 sample is broken by annotation not accessible via reflection on native.
We could potentially reuse Spring AOP infrastructure to identify the reflection entries needed to make aspects working out of the box. We could create a dedicated BeanFactoryInitializationAotProcessor
that would use BeanFactoryAspectJAdvisorsBuilder
to get the List<Advisor>
and process each of them. Maybe we could get the needed information for inference via instanceof PointcutAdvisor
and instanceof AbstractAspectJAdvice
checks.
Sorry for moving that again, but I won't have the bandwidth to tackle that in Spring 6.0 timeframe, so let's target 6.1.
This fix has been tested successfully with a few samples (including spring-aot-smoke-test/framework/aspect
one).
#30529 repro is still broken for reasons that remain to be identified, but this will be handle via the dedicated issue.
Feedback welcome.
I believe this broke the batch AOT sample as follows:
Exception in thread "main" java.lang.NoClassDefFoundError: org/aspectj/lang/annotation/Pointcut
at org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.<clinit>(AbstractAspectJAdvisorFactory.java:61)
at org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.<init>(BeanFactoryAspectJAdvisorsBuilder.java:60)
at org.springframework.aop.aspectj.annotation.AspectJBeanFactoryInitializationAotProcessor.processAheadOfTime(AspectJBeanFactoryInitializationAotProcessor.java:44)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.getContributions(BeanFactoryInitializationAotContributions.java:67)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.<init>(BeanFactoryInitializationAotContributions.java:49)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.<init>(BeanFactoryInitializationAotContributions.java:44)
at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$processAheadOfTime$0(ApplicationContextAotGenerator.java:58)
at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67)
at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53)
at org.springframework.context.aot.ContextAotProcessor.performAotProcessing(ContextAotProcessor.java:106)
at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:84)
at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:49)
at org.springframework.context.aot.AbstractAotProcessor.process(AbstractAotProcessor.java:82)
at org.springframework.boot.SpringApplicationAotProcessor.main(SpringApplicationAotProcessor.java:80)
Caused by: java.lang.ClassNotFoundException: org.aspectj.lang.annotation.Pointcut
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 14 more
The batch sample is failing. I don't know if it is special or if something else is involved.