RootBeanDefinition.getSource() is null in spring-aot-mode.

The simplest example

https://github.com/wangliang181230/example__spring-projects_spring-framework_issue-30017


The classes

public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (beanDefinition.getSource() == null) {
            throw new BeanDefinitionValidationException("The source must not be null");
        }

        // Do something, for example: get an annotation from the MethodMetadata.
    }

}

Reproduce

  1. clone the sample application.
  2. mvn clean install -Pnative.
  3. Run the test ExampleApplicationTest, and it will throw an exception. See MyMergedBeanDefinitionPostProcessor.

env and versions:

  1. OS: Windows 10
  2. GraalVM: graalvm-ce-java17-22.3.1 Windows (amd64)
  3. Spring Boot: 3.0.2
  4. native-maven-plugin: 0.9.20

Comment From: sbrannen

getSource() is permitted to return null, so it's not necessarily an error state.

Though I suppose you're expecting a source to be available in AOT mode if it's available in standard mode.

As a side note, are you aware of the org.springframework.beans.factory.ListableBeanFactory.findAnnotationOnBean(String, Class<A>) method?

Comment From: wangliang181230

getSource() is permitted to return null, so it's not necessarily an error state.

If it is not a bug, I want to know why the MethodMetadata is not loaded in AOT mode?

Though I suppose you're expecting a source to be available in AOT mode if it's available in standard mode.

If AOT mode and standard mode can be consistent, it is best.

As a side note, are you aware of the org.springframework.beans.factory.ListableBeanFactory.findAnnotationOnBean(String, Class<A>) method?

Sorry, I don't know this method. Let me see what it is.

Comment From: sbrannen

If it is not a bug, I want to know why the MethodMetadata is not loaded in AOT mode?

It certainly seems to be a gap in the AOT support which could potentially be considered a bug. Someone in the team will need to take a closer look at the code in question to determine that.

If AOT mode and standard mode can be consistent, it is best.

Yes, I agree with that. Whenever possible, we aim for AOT mode semantics to align with standard Spring runtime semantics.

As a side note, are you aware of the org.springframework.beans.factory.ListableBeanFactory.findAnnotationOnBean(String, Class<A>) method?

Sorry, I don't know this method. Let me see what it is.

I mentioned that, because your example application looks up annotations on the @Bean methods, and ListableBeanFactory.findAnnotationOnBean is typically the preferred mechanism for achieving that.

In other words, if your use case only involves looking up annotations on bean classes and @Bean methods, you should theoretically be able to rework your MergedBeanDefinitionPostProcessor implementation so that it:

  • implements BeanFactoryAware
  • checks that the supplied BeanFactory is an instanceof ListableBeanFactory
  • casts to ListableBeanFactory
  • uses ListableBeanFactory.findAnnotationOnBean to find annotations instead of relying on the beanDefinition.getSource() being an instance of StandardMethodMetadata.

Does that suit your needs as an interim workaround?

Comment From: wangliang181230

I mentioned that, because your example application looks up annotations on the @Bean methods, and ListableBeanFactory.findAnnotationOnBean is typically the preferred mechanism for achieving that.

In other words, if your use case only involves looking up annotations on bean classes and @Bean methods, you should theoretically be able to rework your MergedBeanDefinitionPostProcessor implementation so that it:

  • implements BeanFactoryAware
  • checks that the supplied BeanFactory is an instanceof ListableBeanFactory
  • casts to ListableBeanFactory
  • uses ListableBeanFactory.findAnnotationOnBean to find annotations instead of relying on the beanDefinition.getSource() being an instance of StandardMethodMetadata.

Does that make suit your needs as an interim workaround?

Oh, yes, it looks better. I will try it. Thank you very much.

Comment From: snicoll

In AOT mode, there is no notion of @Configuration class anymore. Some components that are discovered by classpath scanning could have a specific BeanDefinition-subtype that is "lost" in AOT mode as the scanning does not occur.

As discussed here already, your current code should not rely on getSource().