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

AspectJExpressionPointcut#getTargetShadowMatch tentatively creates proxies for every bean #29519

Closed
goafabric opened this issue Nov 18, 2022 · 11 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing
Milestone

Comments

@goafabric
Copy link

So this is a continuation of
spring-attic/spring-native#956
which is still an open issue with Boot 3 (RC2 + latest snapshot)

While simple methods expressions like this just work fine
"execution(public * org.goafabric.calleeservice.aspect.TestComponent.*(..))"

More complex expressions, that e.g. leverage an custom anntotation, don't.
To be more specific .. they work with console applications, but crash the moment wie have WEB on the classpath.
Which somehow explains the error.
Tried to set the jdk proxy interfaces that graalvm demands, but stopped after the 3rd roundtrip because this seems
to be a neverending road ...

I've attached a simple example that works with non native, but crashes on bootstrap of the native image.
What's also a little mind boggeling .. is that i have to register a simple hint for the Test Aspect.
While the aot-smoke-tests don't ...

error.txt
example.zip

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

snicoll commented Nov 18, 2022

@goafabric thanks but that's not a Spring Boot concern. Moving to framework.

@snicoll snicoll transferred this issue from spring-projects/spring-boot Nov 18, 2022
@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing labels Nov 18, 2022
@sbrannen
Copy link
Member

@sbrannen sbrannen changed the title spring native aop support, beyond simple method expressions AOP support in native images, beyond simple method expressions Nov 18, 2022
@goafabric
Copy link
Author

goafabric commented Nov 18, 2022

Thx for answering that fast .. insanely fast ...

#28711 is a little different

It at least explains why i had to set
hints.reflection().registerType(TestAspect.class, MemberCategory.INVOKE_DECLARED_METHODS).

But with this the invocation works .. for all use cases as long as it is a simple console application.

Combine it with web and it crashes. So I guess this is a little different.

@sbrannen sbrannen added this to the Triage Queue milestone Nov 18, 2022
@sdeleuze sdeleuze self-assigned this Jan 4, 2023
@sdeleuze sdeleuze changed the title AOP support in native images, beyond simple method expressions AspectJExpressionPointcut#getTargetShadowMatch tentatively create proxies for every bean Jan 4, 2023
@sdeleuze sdeleuze removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 4, 2023
@sdeleuze sdeleuze modified the milestones: Triage Queue, 6.0.4 Jan 4, 2023
@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 4, 2023

That's an interesting one. We have:

@Component
@Aspect
public class TestAspect {

    @Around("@within(org.goafabric.calleeservice.aspect.TestAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();
        } finally {
            final Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            System.err.println("aspects wrapped method : " + toString(method));
        }
    }
    // ...
}

The sample indeed fails with errors like:

Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory, interface org.springframework.context.ResourceLoaderAware, interface org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory, interface org.springframework.boot.web.server.ConfigurableWebServerFactory] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:89) ~[na:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.proxy.DynamicProxySupport.getProxyClass(DynamicProxySupport.java:171) ~[na:na]
        at java.base@17.0.5/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[callee-service:na]
        at java.base@17.0.5/java.lang.reflect.Proxy.getProxyClass(Proxy.java:398) ~[callee-service:na]
        at org.springframework.util.ClassUtils.createCompositeInterface(ClassUtils.java:783) ~[na:na]
        at org.springframework.aop.aspectj.AspectJExpressionPointcut.getTargetShadowMatch(AspectJExpressionPointcut.java:432) ~[na:na]
        at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:290) ~[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) ~[callee-service:6.0.3]
        at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:97) ~[callee-service:6.0.3]
        at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:78) ~[callee-service:6.0.3]
        at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:366) ~[callee-service:6.0.3]
        at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:318) ~[callee-service:6.0.3]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:435) ~[callee-service:6.0.3]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1754) ~[callee-service:6.0.3]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[callee-service:6.0.3]
        ... 16 common frames omitted

So I began to add more proxy hints, but at some point I stopped because even with that hint configuration, that's not enough to make it work.

 hints.proxies()
                    .registerJdkProxy(OrderedFilter.class, Filter.class, BeanNameAware.class, EnvironmentAware.class,
                            EnvironmentCapable.class, ServletContextAware.class, InitializingBean.class, DisposableBean.class)
                    .registerJdkProxy(WebServerFactoryCustomizer.class, Ordered.class)
                    .registerJdkProxy(ApplicationContextAware.class, EnvironmentCapable.class, EnvironmentAware.class,
                            Servlet.class, ServletConfig.class, Serializable.class)
                    .registerJdkProxy(DispatcherServletPath.class, ServletContextInitializer.class, Ordered.class)
                    .registerJdkProxy(ErrorPageRegistrar.class, Ordered.class)
                    .registerJdkProxy(ViewResolver.class, Ordered.class, ServletContextAware.class, ApplicationContextAware.class)
                    .registerJdkProxy(ErrorViewResolver.class, Ordered.class)
                    .registerJdkProxy(ErrorAttributes.class, Ordered.class)
                    .registerJdkProxy(ErrorAttributes.class, HandlerExceptionResolver.class, Ordered.class)
                    .registerJdkProxy(WebMvcConfigurer.class, ServletContextAware.class)
                    .registerJdkProxy(ResourceLoaderAware.class, ApplicationContextAware.class, ServletContextAware.class)
                    .registerJdkProxy(ConfigurableTomcatWebServerFactory.class, ResourceLoaderAware.class,
                            ConfigurableServletWebServerFactory.class, ConfigurableWebServerFactory.class);

@jhoeller It looks like AspectJExpressionPointcut#getTargetShadowMatch tentatively create proxies for every bean in this try catch block. I am wondering about the performance impact on the JVM, and on native that means we would need to create proxy hints for every bean, leading to massive compatibility headaches and increased footprint. Is this something we could refine from your point of view?

@sbrannen sbrannen changed the title AspectJExpressionPointcut#getTargetShadowMatch tentatively create proxies for every bean AspectJExpressionPointcut#getTargetShadowMatch tentatively creates proxies for every bean Jan 9, 2023
@jhoeller
Copy link
Contributor

jhoeller commented Jan 9, 2023

That composite interface creation is only really needed for default methods in interfaces and similar scenarios where interface-based method implementations are inherited rather than implemented locally. I've added a corresponding condition to reduce the creation of composite interfaces to actual method references in the current pointcut, that is, only when the pointcut actually refers to a non-overridden default method or the like. This should eliminate all of the common cases above.

@sdeleuze
Copy link
Contributor

I confirm the example is now working as expected with method hints (hints.reflection().registerType(TestAspect.class, MemberCategory.INVOKE_DECLARED_METHODS);) which will be inferred via #28711.

@goafabric
Copy link
Author

@sdeleuze
thank you for finally having this fixed
is there any kind of way i can try it out myself already ?

boot 3.0.2-snapshot still seems to rely on framework-core 6.0.3
and including framework-core 6.0.4-SNAPSHOT did not solve the issue

or do i just have to wait for a release ? :)

thx

@sdeleuze
Copy link
Contributor

Thanks should go to @jhoeller for the fix ;-)

You can override the Framework version in Boot, otherwise just wait a bit, Spring Framework 6.0.4 has been released today and will be picked up by Boot shortly.

@goafabric
Copy link
Author

yes i saw it on twitter yesterday evening, thx 4 letting me know and of course also to @jhoeller

@goafabric
Copy link
Author

goafabric commented Jan 15, 2023

@sdeleuze
I can happily report that everything is working now with boot 3.0.2-SNAPSHOT
Even more complex applications finally including CircuitBreaker Annotation
So thx again to both of you.

As a sugar on top .. would it be possible to get rid of the "hints.reflection().registerType" ?
Because basically everything annotated with Aspect should qualify already qualify for reflection ?

Or .. "at least" as a fallback, at least have a working variant of "RegisterForReflectionBinding"

Then it would also be nice to have spring cloud resilience4j working out of the box.
I opened an issue a while ago, that was just closed
spring-cloud/spring-cloud-circuitbreaker#159

And this basically needs
" hints.reflection().registerType(io.github.resilience4j.spring6.circuitbreaker.configure.CircuitBreakerAspect.class, builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));"

@sdeleuze
Copy link
Contributor

Please create a related issue with me mentioned with a minimal reproducer and a detailed description of the type hint inference you would expect.

mdeinum pushed a commit to mdeinum/spring-framework that referenced this issue Jun 29, 2023
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
Projects
None yet
Development

No branches or pull requests

6 participants