The ListableBeanFactory
has these methods:
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
Which are useful in cases like this:
@Bean
@GlobalChannelInterceptor(patterns = "dateChannel")
WireTap loggingWireTap() {
....
}
@Bean
@IntegrationConverter
Converter<Date, Integer> currentSeconds() {
...
}
After AOT has processed those beans and generated respective functional substitutions, there is no annotation info on bean definition anymore: an instance supplier just calls the target factory method via lambda:
/**
* Get the bean definition for 'currentSeconds'
*/
public static BeanDefinition getCurrentSecondsBeanDefinition() {
ResolvableType beanType = ResolvableType.forClassWithGenerics(Converter.class, Date.class, Integer.class);
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(InstanceSupplier.of(IntegrationApplication__BeanDefinitions::getCurrentSecondsInstance));
return beanDefinition;
}
/**
* Get the bean instance for 'currentSeconds'.
*/
private static Converter getCurrentSecondsInstance(RegisteredBean registeredBean) {
return registeredBean.getBeanFactory().getBean(IntegrationApplication.class).currentSeconds();
}
I workaround it with some Registration
bean definition from the BeanDefinitionRegistryPostProcessor
at the moment and that's probably will let me to avoid some potential reflection for those annotations at runtime, but I believe there might be some use-cases when the mentioned BeanFactory
API is called at runtime.
Comment From: snicoll
I believe that is a regression of how BeanDefinitions are registered.
Previously, we would use the resolved factory method as an input for the bean definition and I think this has been lost for beans that do not need autowiring as it seems to be autowiring specific now.
Comment From: snicoll
paging @philwebb
Comment From: snicoll
So our plan is to change the signature of the method that provides a bean instance so that it has to provide the factoryMethod
reference one way or the other. While brainstorming, we've also realized that AutowiredInstantiationArgumentsResolver
might not be the ideal name for what that class does.
Rather than returning the bean instance and using InstanceSupplier.of
we intend to return an instance of InstanceSupplier
that takes care of the exacutable to use, something like:
/**
* Create the bean instance supplier for 'restTemplateClientService'.
*/
private static BeanInstanceSupplier getRestTemplateClientServiceInstanceSupplier() {
return BeanInstanceSupplier
.forConstructor(RestTemplateBuilder.class)
.withGenerator(args -> new RestTemplateClientService(args.get(0)));
}
Comment From: snicoll
Unfortunately, the fix breaks other use cases as the supplier is composed in the caller and we now return a InstanceSupplier<Object>
whereas previously it was an actual typed instance. Example code:
InstanceSupplier<WebMvcAutoConfiguration.EnableWebMvcConfiguration> instanceSupplier = WebMvcAutoConfiguration_EnableWebMvcConfiguration__BeanDefinitions.getEnableWebMvcConfigurationInstanceSupplier();
instanceSupplier = instanceSupplier.andThen(WebMvcAutoConfiguration_EnableWebMvcConfiguration__Autowiring::apply);
Thanks @OlgaMaciaszek for the report!
Comment From: snicoll
This sample showcases the problem:
package org.springframework;
import org.springframework.beans.factory.aot.BeanInstanceSupplier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.InstanceSupplier;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
public class Sample {
/**
* Get the bean definition for 'sampleConfiguration'
*/
public static BeanDefinition getSampleConfigurationBeanDefinition() {
Class<?> beanType = SampleConfiguration.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass", "lite");
InstanceSupplier<SampleConfiguration> instanceSupplier = getSampleConfigurationInstanceSupplier();
instanceSupplier = instanceSupplier.andThen(SampleConfiguration__Autowiring::apply);
beanDefinition.setInstanceSupplier(instanceSupplier);
return beanDefinition;
}
private static BeanInstanceSupplier getSampleConfigurationInstanceSupplier() {
return BeanInstanceSupplier.forConstructor().withGenerator(SampleConfiguration::new);
}
static class SampleConfiguration {
}
static class SampleConfiguration__Autowiring {
static SampleConfiguration apply(RegisteredBean registeredBean, SampleConfiguration sampleConfiguration) {
// autowiring stuff
return sampleConfiguration;
}
}
}
Comment From: snicoll
DefaultBeanRegistrationCodeFragments
is creating an InstanceSupplier
with the bean class. If the bean exposes generic information, these are lost which could be a problem for the thing post-processing the bean (+ uncheck warning). That probably used to apply to the method that created the instance previously.
Comment From: philwebb
I've updated InstanceSupplier.andThen()
so that the factory method isn't lost. We still need to fix the generics.
Comment From: philwebb
I've added a generic to BeanInstanceSupplier
which I hope will fix this. I'd like to leave this one open to see if we can improve the generated code even more.
Comment From: snicoll
Phil and I discussed this and agreed to close this issue as the reported problem is fixed. We've brainstormed on how we could make it better, see #28875.