Skip to content

Commit

Permalink
Provide hierarchy traversal support for getBeanNamesForAnnotation
Browse files Browse the repository at this point in the history
Issue: SPR-15923
  • Loading branch information
snicoll committed Sep 3, 2017
1 parent f0198f3 commit 71182ab
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.beans.factory;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -353,6 +354,37 @@ public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<
return uniqueBean(type, beansOfType);
}

/**
* Get all bean names whose {@code Class} has the supplied {@link Annotation}
* type, including those defined in ancestor factories, without creating any bean
* instances yet. Will return unique names in case of overridden bean definitions.
* @param lbf the bean factory
* @param annotationType the type of annotation to look for
* @return the array of matching bean names, or an empty array if none
* @since 5.0
*/
public static String[] beanNamesForAnnotationIncludingAncestors(
ListableBeanFactory lbf, Class<? extends Annotation> annotationType) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForAnnotation(annotationType);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForAnnotationIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), annotationType);
List<String> resultList = new ArrayList<>();
resultList.addAll(Arrays.asList(result));
for (String beanName : parentResult) {
if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
resultList.add(beanName);
}
}
result = StringUtils.toStringArray(resultList);
}
}
return result;
}

/**
* Return a single bean of the given type or subtypes, also picking up beans
* defined in ancestor bean factories if the current bean factory is a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,8 +28,10 @@
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.core.io.Resource;
import org.springframework.tests.sample.beans.AnnotatedBean;
import org.springframework.tests.sample.beans.ITestBean;
import org.springframework.tests.sample.beans.IndexedTestBean;
import org.springframework.tests.sample.beans.TestAnnotation;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.tests.sample.beans.factory.DummyFactory;
import org.springframework.util.ObjectUtils;
Expand Down Expand Up @@ -91,8 +93,8 @@ public void testHierarchicalCountBeansWithOverride() throws Exception {
// Leaf count
assertTrue(this.listableBeanFactory.getBeanDefinitionCount() == 1);
// Count minus duplicate
assertTrue("Should count 7 beans, not " + BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory),
BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory) == 7);
assertTrue("Should count 8 beans, not " + BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory),
BeanFactoryUtils.countBeansIncludingAncestors(this.listableBeanFactory) == 8);
}

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

@Test
public void testHierarchicalNamesForAnnotationWithNoMatch() throws Exception {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, Override.class));
assertEquals(0, names.size());
}

@Test
public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() throws Exception {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
assertEquals(1, names.size());
assertTrue(names.contains("annotatedBean"));
// Distinguish from default ListableBeanFactory behavior
assertTrue(listableBeanFactory.getBeanNamesForAnnotation(TestAnnotation.class).length == 0);
}

@Test
public void testGetBeanNamesForAnnotationWithOverride() throws Exception {
AnnotatedBean annotatedBean = new AnnotatedBean();
this.listableBeanFactory.registerSingleton("anotherAnnotatedBean", annotatedBean);
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
assertEquals(2, names.size());
assertTrue(names.contains("annotatedBean"));
assertTrue(names.contains("anotherAnnotatedBean"));
}

@Test
public void testADependencies() {
String[] deps = this.dependentBeansFactory.getDependentBeans("a");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.tests.sample.beans;

/**
* @author Stephane Nicoll
*/
@TestAnnotation
public class AnnotatedBean {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.tests.sample.beans;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* @author Stephane Nicoll
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

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

<bean id="annotatedBean" class="org.springframework.tests.sample.beans.AnnotatedBean"/>

<!-- Overridden by next factory -->
<bean id="test" class="org.springframework.tests.sample.beans.TestBean">
<property name="name"><value>custom</value></property>
Expand Down

0 comments on commit 71182ab

Please sign in to comment.