Skip to content

Commit 71182ab

Browse files
committed
Provide hierarchy traversal support for getBeanNamesForAnnotation
Issue: SPR-15923
1 parent f0198f3 commit 71182ab

File tree

5 files changed

+118
-3
lines changed

5 files changed

+118
-3
lines changed

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.beans.factory;
1818

19+
import java.lang.annotation.Annotation;
1920
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.LinkedHashMap;
@@ -353,6 +354,37 @@ public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<
353354
return uniqueBean(type, beansOfType);
354355
}
355356

357+
/**
358+
* Get all bean names whose {@code Class} has the supplied {@link Annotation}
359+
* type, including those defined in ancestor factories, without creating any bean
360+
* instances yet. Will return unique names in case of overridden bean definitions.
361+
* @param lbf the bean factory
362+
* @param annotationType the type of annotation to look for
363+
* @return the array of matching bean names, or an empty array if none
364+
* @since 5.0
365+
*/
366+
public static String[] beanNamesForAnnotationIncludingAncestors(
367+
ListableBeanFactory lbf, Class<? extends Annotation> annotationType) {
368+
Assert.notNull(lbf, "ListableBeanFactory must not be null");
369+
String[] result = lbf.getBeanNamesForAnnotation(annotationType);
370+
if (lbf instanceof HierarchicalBeanFactory) {
371+
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
372+
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
373+
String[] parentResult = beanNamesForAnnotationIncludingAncestors(
374+
(ListableBeanFactory) hbf.getParentBeanFactory(), annotationType);
375+
List<String> resultList = new ArrayList<>();
376+
resultList.addAll(Arrays.asList(result));
377+
for (String beanName : parentResult) {
378+
if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
379+
resultList.add(beanName);
380+
}
381+
}
382+
result = StringUtils.toStringArray(resultList);
383+
}
384+
}
385+
return result;
386+
}
387+
356388
/**
357389
* Return a single bean of the given type or subtypes, also picking up beans
358390
* defined in ancestor bean factories if the current bean factory is a

spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -28,8 +28,10 @@
2828
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
2929
import org.springframework.cglib.proxy.NoOp;
3030
import org.springframework.core.io.Resource;
31+
import org.springframework.tests.sample.beans.AnnotatedBean;
3132
import org.springframework.tests.sample.beans.ITestBean;
3233
import org.springframework.tests.sample.beans.IndexedTestBean;
34+
import org.springframework.tests.sample.beans.TestAnnotation;
3335
import org.springframework.tests.sample.beans.TestBean;
3436
import org.springframework.tests.sample.beans.factory.DummyFactory;
3537
import org.springframework.util.ObjectUtils;
@@ -91,8 +93,8 @@ public void testHierarchicalCountBeansWithOverride() throws Exception {
9193
// Leaf count
9294
assertTrue(this.listableBeanFactory.getBeanDefinitionCount() == 1);
9395
// Count minus duplicate
94-
assertTrue("Should count 7 beans, not " + BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory),
95-
BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory) == 7);
96+
assertTrue("Should count 8 beans, not " + BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory),
97+
BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory) == 8);
9698
}
9799

98100
@Test
@@ -265,6 +267,34 @@ public void testHierarchicalResolutionWithOverride() throws Exception {
265267
assertEquals(this.listableBeanFactory.getBean("&testFactory2"), beans.get("&testFactory2"));
266268
}
267269

270+
@Test
271+
public void testHierarchicalNamesForAnnotationWithNoMatch() throws Exception {
272+
List<String> names = Arrays.asList(
273+
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, Override.class));
274+
assertEquals(0, names.size());
275+
}
276+
277+
@Test
278+
public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() throws Exception {
279+
List<String> names = Arrays.asList(
280+
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
281+
assertEquals(1, names.size());
282+
assertTrue(names.contains("annotatedBean"));
283+
// Distinguish from default ListableBeanFactory behavior
284+
assertTrue(listableBeanFactory.getBeanNamesForAnnotation(TestAnnotation.class).length == 0);
285+
}
286+
287+
@Test
288+
public void testGetBeanNamesForAnnotationWithOverride() throws Exception {
289+
AnnotatedBean annotatedBean = new AnnotatedBean();
290+
this.listableBeanFactory.registerSingleton("anotherAnnotatedBean", annotatedBean);
291+
List<String> names = Arrays.asList(
292+
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
293+
assertEquals(2, names.size());
294+
assertTrue(names.contains("annotatedBean"));
295+
assertTrue(names.contains("anotherAnnotatedBean"));
296+
}
297+
268298
@Test
269299
public void testADependencies() {
270300
String[] deps = this.dependentBeansFactory.getDependentBeans("a");
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2002-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.tests.sample.beans;
18+
19+
/**
20+
* @author Stephane Nicoll
21+
*/
22+
@TestAnnotation
23+
public class AnnotatedBean {
24+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2002-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.tests.sample.beans;
18+
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
22+
/**
23+
* @author Stephane Nicoll
24+
*/
25+
@Retention(RetentionPolicy.RUNTIME)
26+
public @interface TestAnnotation {
27+
}

spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-root.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
<bean id="indexedBean" class="org.springframework.tests.sample.beans.IndexedTestBean"/>
1212

13+
<bean id="annotatedBean" class="org.springframework.tests.sample.beans.AnnotatedBean"/>
14+
1315
<!-- Overridden by next factory -->
1416
<bean id="test" class="org.springframework.tests.sample.beans.TestBean">
1517
<property name="name"><value>custom</value></property>

0 commit comments

Comments
 (0)