Skip to content

Commit e0fe32a

Browse files
committed
Detect factory method annotations in getBeanNamesForAnnotation and co
As of 5.2, ListableBeanFactory.findAnnotationOnBean and its retrieval companions getBeanNamesForAnnotation and getBeansWithAnnotation detect annotations on @bean methods as well. Closes gh-22541
1 parent dee88d9 commit e0fe32a

File tree

3 files changed

+44
-28
lines changed

3 files changed

+44
-28
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -248,8 +248,10 @@ <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSin
248248
* <p>Note that this method considers objects created by FactoryBeans, which means
249249
* that FactoryBeans will get initialized in order to determine their object type.
250250
* @param annotationType the type of annotation to look for
251+
* (at class, interface or factory method level of the specified bean)
251252
* @return the names of all matching beans
252253
* @since 4.0
254+
* @see #findAnnotationOnBean
253255
*/
254256
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
255257

@@ -259,22 +261,27 @@ <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSin
259261
* <p>Note that this method considers objects created by FactoryBeans, which means
260262
* that FactoryBeans will get initialized in order to determine their object type.
261263
* @param annotationType the type of annotation to look for
264+
* (at class, interface or factory method level of the specified bean)
262265
* @return a Map with the matching beans, containing the bean names as
263266
* keys and the corresponding bean instances as values
264267
* @throws BeansException if a bean could not be created
265268
* @since 3.0
269+
* @see #findAnnotationOnBean
266270
*/
267271
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
268272

269273
/**
270-
* Find an {@link Annotation} of {@code annotationType} on the specified
271-
* bean, traversing its interfaces and super classes if no annotation can be
272-
* found on the given class itself.
274+
* Find an {@link Annotation} of {@code annotationType} on the specified bean,
275+
* traversing its interfaces and super classes if no annotation can be found on
276+
* the given class itself, as well as checking the bean's factory method (if any).
273277
* @param beanName the name of the bean to look for annotations on
274-
* @param annotationType the annotation class to look for
278+
* @param annotationType the type of annotation to look for
279+
* (at class, interface or factory method level of the specified bean)
275280
* @return the annotation of the given type if found, or {@code null} otherwise
276281
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
277282
* @since 3.0
283+
* @see #getBeanNamesForAnnotation
284+
* @see #getBeansWithAnnotation
278285
*/
279286
@Nullable
280287
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

+21-22
Original file line numberDiff line numberDiff line change
@@ -678,23 +678,32 @@ private <A extends Annotation> MergedAnnotation<A> findMergedAnnotationOnBean(
678678

679679
Class<?> beanType = getType(beanName);
680680
if (beanType != null) {
681-
MergedAnnotation<A> annotation = MergedAnnotations.from(beanType,
682-
SearchStrategy.EXHAUSTIVE).get(annotationType);
681+
MergedAnnotation<A> annotation =
682+
MergedAnnotations.from(beanType, SearchStrategy.EXHAUSTIVE).get(annotationType);
683683
if (annotation.isPresent()) {
684684
return annotation;
685685
}
686686
}
687687
if (containsBeanDefinition(beanName)) {
688-
BeanDefinition bd = getMergedBeanDefinition(beanName);
689-
if (bd instanceof AbstractBeanDefinition) {
690-
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
691-
if (abd.hasBeanClass()) {
692-
Class<?> beanClass = abd.getBeanClass();
693-
if (beanClass != beanType) {
694-
return MergedAnnotations.from(beanClass, SearchStrategy.EXHAUSTIVE).get(annotationType);
688+
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
689+
if (bd.hasBeanClass()) {
690+
Class<?> beanClass = bd.getBeanClass();
691+
if (beanClass != beanType) {
692+
MergedAnnotation<A> annotation =
693+
MergedAnnotations.from(beanClass, SearchStrategy.EXHAUSTIVE).get(annotationType);
694+
if (annotation.isPresent()) {
695+
return annotation;
695696
}
696697
}
697698
}
699+
Method factoryMethod = bd.getResolvedFactoryMethod();
700+
if (factoryMethod != null) {
701+
MergedAnnotation<A> annotation =
702+
MergedAnnotations.from(factoryMethod, SearchStrategy.EXHAUSTIVE).get(annotationType);
703+
if (annotation.isPresent()) {
704+
return annotation;
705+
}
706+
}
698707
}
699708
return MergedAnnotation.missing();
700709
}
@@ -1988,10 +1997,11 @@ public FactoryAwareOrderSourceProvider(Map<Object, String> instancesToBeanNames)
19881997
@Override
19891998
@Nullable
19901999
public Object getOrderSource(Object obj) {
1991-
RootBeanDefinition beanDefinition = getRootBeanDefinition(this.instancesToBeanNames.get(obj));
1992-
if (beanDefinition == null) {
2000+
String beanName = this.instancesToBeanNames.get(obj);
2001+
if (beanName == null || !containsBeanDefinition(beanName)) {
19932002
return null;
19942003
}
2004+
RootBeanDefinition beanDefinition = getMergedLocalBeanDefinition(beanName);
19952005
List<Object> sources = new ArrayList<>(2);
19962006
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
19972007
if (factoryMethod != null) {
@@ -2003,17 +2013,6 @@ public Object getOrderSource(Object obj) {
20032013
}
20042014
return sources.toArray();
20052015
}
2006-
2007-
@Nullable
2008-
private RootBeanDefinition getRootBeanDefinition(@Nullable String beanName) {
2009-
if (beanName != null && containsBeanDefinition(beanName)) {
2010-
BeanDefinition bd = getMergedBeanDefinition(beanName);
2011-
if (bd instanceof RootBeanDefinition) {
2012-
return (RootBeanDefinition) bd;
2013-
}
2014-
}
2015-
return null;
2016-
}
20172016
}
20182017

20192018
}

spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -129,6 +129,16 @@ public void testCustomWithAttributeOverride() {
129129
assertThat(pojo.testBean.getName(), equalTo("interesting"));
130130
}
131131

132+
@Test
133+
public void testBeanNamesForAnnotation() {
134+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(StandardConfig.class);
135+
assertArrayEquals(new String[] {"beanMethodQualificationTests.StandardConfig"},
136+
ctx.getBeanNamesForAnnotation(Configuration.class));
137+
assertArrayEquals(new String[] {}, ctx.getBeanNamesForAnnotation(Scope.class));
138+
assertArrayEquals(new String[] {"testBean1"}, ctx.getBeanNamesForAnnotation(Lazy.class));
139+
assertArrayEquals(new String[] {"testBean2"}, ctx.getBeanNamesForAnnotation(Boring.class));
140+
}
141+
132142

133143
@Configuration
134144
static class StandardConfig {

0 commit comments

Comments
 (0)