-
Notifications
You must be signed in to change notification settings - Fork 38.3k
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
BeanFactory.get*ForAnnotation should take @Bean metadata into account #22541
Comments
Are you referring explicitly to Also, if we were to introduce such support, would the introduction of a new method specific to your proposed use case be sufficient/acceptable? |
I'm indeed referring to those methods (I also believe they eventually delegate to the same internal methods). I would not expect additional methods to be added, but if that is more convenient (or less of a surprise to existing users) then I'm perfectly fine with that. |
Yes,
Aiming for the element of least surprise is exactly my intention here: changing the behavior of those existing methods would technically be a breaking change since such invocations would start to return things that they previously did not. |
If you want to implement this on your own, it is technically possible. For example, the following works for public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Class<? extends Annotation> annotationType = MyAnnotation.class;
Arrays.stream(beanFactory.getBeanDefinitionNames())//
.map(beanFactory::getBeanDefinition)//
.filter(bd -> !bd.isAbstract())//
.filter(bd -> bd.getFactoryMethodName() != null)//
.filter(bd -> bd.getFactoryBeanName() != null)//
.filter(bd -> isBeanMethodAnnotated(beanFactory, bd, annotationType))//
.forEach(bd -> System.out.println("@Bean method " + bd.getFactoryMethodName()
+ " is annotated with @" + annotationType.getSimpleName() + "."));
}
private boolean isBeanMethodAnnotated(ConfigurableListableBeanFactory beanFactory,
BeanDefinition beanDefinition, Class<? extends Annotation> annotationType) {
BeanDefinition factoryBeanDefinition = beanFactory.getBeanDefinition(beanDefinition.getFactoryBeanName());
if (factoryBeanDefinition instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abd = (AbstractBeanDefinition) factoryBeanDefinition;
if (abd.hasBeanClass()) {
Class<?> factoryClass = ClassUtils.getUserClass(abd.getBeanClass());
String factoryMethodName = beanDefinition.getFactoryMethodName();
Method factoryMethod = ReflectionUtils.findMethod(factoryClass, factoryMethodName);
return AnnotatedElementUtils.isAnnotated(factoryMethod, Bean.class)
&& AnnotatedElementUtils.isAnnotated(factoryMethod, annotationType);
}
}
return false;
}
} If you want to support In any case, the above serves as a proof of concept. |
Tentatively slated for 5.2 M1 for further investigation and discussion |
Wouldn't it be nicer to detect the annotations while scanning/detecting beans and store them in the For me as a user (I know I might not be the average user :) ), it doesn't really matter if I specify metadata on the |
@mdeinum, it looks like Spring Boot already has the support you're looking for... What do you think about that? |
Update: due to changes in public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Class<? extends Annotation> annotationType = MyAnnotation.class;
findAnnotatedBeanDefinitions(beanFactory, annotationType)//
.forEach(bd -> System.out.println("@Bean method " + bd.getFactoryMethodName()
+ " is annotated with @" + annotationType.getSimpleName() + "."));
}
private List<BeanDefinition> findAnnotatedBeanDefinitions(ConfigurableListableBeanFactory beanFactory,
Class<? extends Annotation> annotationType) {
return Arrays.stream(beanFactory.getBeanDefinitionNames())//
.map(beanFactory::getBeanDefinition)//
.filter(bd -> !bd.isAbstract())//
.filter(RootBeanDefinition.class::isInstance)//
.map(RootBeanDefinition.class::cast)//
.filter(bd -> isAnnotatedBeanMethod(bd, annotationType))//
.collect(toList());
}
private boolean isAnnotatedBeanMethod(RootBeanDefinition beanDefinition,
Class<? extends Annotation> annotationType) {
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
return factoryMethod != null && AnnotatedElementUtils.isAnnotated(factoryMethod, Bean.class)
&& AnnotatedElementUtils.isAnnotated(factoryMethod, annotationType);
}
} |
This commit makes use of the new getResolvedFactoryMethod() method in RootBeanDefinition. See spring-projectsgh-22541
On review, I'm in favor of redefining As mentioned above, this fits nicely with the |
SGTM 👍 |
I'm currently writing a small
BeanFactoryPostProcessor
and for that I need to process annotations on@Bean
methods. Or at least detect the bean definition based on that metadata. I was expecting thatgetBeanNamesForAnnotation
(and friends) would also take annotations on@Bean
methods into consideration. However it only works for annotated types.So when doing something like this.
It works. But doing the same on an
@Bean
annotated method doesn't work.It would be nice if the methods would return beans (or bean definitions) annotated throuhg
@Bean
methods as well. Especially if we want to apply this to beans that aren't under our own control (i.e. external libraries).For the Spring annotations like
@Lazy
,@Primary
etc. this already works but those are handled in specialized cases when parsing the configuration files.@Lazy
on a component works the same as@Lazy
on a@Bean
method.In this particulair case I wanted to detect beans with a special annotation so that we could register them in JNDI. It works when annotating the component with our specialized annotation not when using the annotation on
@Bean
methods. This is especially annoying for classes that are outside of our control.Currently the only way is to extend the class only for adding the annotation.
The text was updated successfully, but these errors were encountered: