Skip to content

Commit 70472b3

Browse files
committed
Merge branch '1.4.x' into 1.5.x
2 parents 4390c81 + 59d3a79 commit 70472b3

File tree

4 files changed

+175
-39
lines changed

4 files changed

+175
-39
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java

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

1717
package org.springframework.boot.autoconfigure.condition;
1818

19+
import java.lang.annotation.Annotation;
1920
import java.lang.reflect.Method;
2021
import java.util.Arrays;
2122
import java.util.HashMap;
@@ -40,6 +41,7 @@
4041
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4142
import org.springframework.beans.factory.support.RootBeanDefinition;
4243
import org.springframework.core.ResolvableType;
44+
import org.springframework.core.annotation.AnnotationUtils;
4345
import org.springframework.core.type.MethodMetadata;
4446
import org.springframework.core.type.StandardMethodMetadata;
4547
import org.springframework.util.Assert;
@@ -85,7 +87,7 @@ private BeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
8587
* @param beanFactory the source bean factory
8688
* @return the {@link BeanTypeRegistry} for the given bean factory
8789
*/
88-
public static BeanTypeRegistry create(ListableBeanFactory beanFactory) {
90+
static BeanTypeRegistry get(ListableBeanFactory beanFactory) {
8991
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
9092
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
9193
Assert.isTrue(listableBeanFactory.isAllowEagerClassLoading(),
@@ -101,26 +103,39 @@ public static BeanTypeRegistry create(ListableBeanFactory beanFactory) {
101103

102104
/**
103105
* Return the names of beans matching the given type (including subclasses), judging
104-
* from either bean definitions or the value of {@code getObjectType} in the case of
105-
* FactoryBeans. Will include singletons but not cause early bean initialization.
106+
* from either bean definitions or the value of {@link FactoryBean#getObjectType()} in
107+
* the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not
108+
* cause early bean initialization.
106109
* @param type the class or interface to match (must not be {@code null})
107110
* @return the names of beans (or objects created by FactoryBeans) matching the given
108111
* object type (including subclasses), or an empty set if none
109112
*/
110113
Set<String> getNamesForType(Class<?> type) {
111-
if (this.lastBeanDefinitionCount != this.beanFactory.getBeanDefinitionCount()) {
112-
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
113-
while (names.hasNext()) {
114-
String name = names.next();
115-
if (!this.beanTypes.containsKey(name)) {
116-
addBeanType(name);
117-
}
114+
updateTypesIfNecessary();
115+
Set<String> matches = new LinkedHashSet<String>();
116+
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
117+
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
118+
matches.add(entry.getKey());
118119
}
119-
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
120120
}
121+
return matches;
122+
}
123+
124+
/**
125+
* Returns the names of beans annotated with the given {@code annotation}, judging
126+
* from either bean definitions or the value of {@link FactoryBean#getObjectType()} in
127+
* the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not
128+
* cause early bean initialization.
129+
* @param annotation the annotation to match (must not be {@code null})
130+
* @return the names of beans (or objects created by FactoryBeans) annoated with the
131+
* given annotation, or an empty set if none
132+
*/
133+
Set<String> getNamesForAnnotation(Class<? extends Annotation> annotation) {
134+
updateTypesIfNecessary();
121135
Set<String> matches = new LinkedHashSet<String>();
122136
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
123-
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
137+
if (entry.getValue() != null && AnnotationUtils
138+
.findAnnotation(entry.getValue(), annotation) != null) {
124139
matches.add(entry.getKey());
125140
}
126141
}
@@ -183,6 +198,19 @@ private boolean requiresEagerInit(String factoryBeanName) {
183198
&& !this.beanFactory.containsSingleton(factoryBeanName));
184199
}
185200

201+
private void updateTypesIfNecessary() {
202+
if (this.lastBeanDefinitionCount != this.beanFactory.getBeanDefinitionCount()) {
203+
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
204+
while (names.hasNext()) {
205+
String name = names.next();
206+
if (!this.beanTypes.containsKey(name)) {
207+
addBeanType(name);
208+
}
209+
}
210+
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
211+
}
212+
}
213+
186214
/**
187215
* Attempt to guess the type that a {@link FactoryBean} will return based on the
188216
* generics in its method signature.

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Arrays;
2222
import java.util.Collection;
2323
import java.util.Collections;
24+
import java.util.HashSet;
2425
import java.util.LinkedHashSet;
2526
import java.util.List;
2627
import java.util.Set;
@@ -56,8 +57,6 @@
5657
@Order(Ordered.LOWEST_PRECEDENCE)
5758
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {
5859

59-
private static final String[] NO_BEANS = {};
60-
6160
/**
6261
* Bean definition attribute name for factory beans to signal their product type (if
6362
* known and it can't be deduced from the factory bean class).
@@ -183,7 +182,7 @@ private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory,
183182

184183
private void collectBeanNamesForType(Set<String> result,
185184
ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
186-
result.addAll(BeanTypeRegistry.create(beanFactory).getNamesForType(type));
185+
result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
187186
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
188187
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
189188
.getParentBeanFactory();
@@ -197,34 +196,32 @@ private void collectBeanNamesForType(Set<String> result,
197196
private String[] getBeanNamesForAnnotation(
198197
ConfigurableListableBeanFactory beanFactory, String type,
199198
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
200-
String[] result = NO_BEANS;
199+
Set<String> names = new HashSet<String>();
201200
try {
202201
@SuppressWarnings("unchecked")
203-
Class<? extends Annotation> typeClass = (Class<? extends Annotation>) ClassUtils
202+
Class<? extends Annotation> annotationType = (Class<? extends Annotation>) ClassUtils
204203
.forName(type, classLoader);
205-
result = beanFactory.getBeanNamesForAnnotation(typeClass);
206-
if (considerHierarchy) {
207-
if (beanFactory
208-
.getParentBeanFactory() instanceof ConfigurableListableBeanFactory) {
209-
String[] parentResult = getBeanNamesForAnnotation(
210-
(ConfigurableListableBeanFactory) beanFactory
211-
.getParentBeanFactory(),
212-
type, classLoader, true);
213-
List<String> resultList = new ArrayList<String>();
214-
resultList.addAll(Arrays.asList(result));
215-
for (String beanName : parentResult) {
216-
if (!resultList.contains(beanName)
217-
&& !beanFactory.containsLocalBean(beanName)) {
218-
resultList.add(beanName);
219-
}
220-
}
221-
result = StringUtils.toStringArray(resultList);
222-
}
223-
}
224-
return result;
204+
collectBeanNamesForAnnotation(names, beanFactory, annotationType,
205+
considerHierarchy);
225206
}
226-
catch (ClassNotFoundException ex) {
227-
return NO_BEANS;
207+
catch (ClassNotFoundException e) {
208+
// Continue
209+
}
210+
return StringUtils.toStringArray(names);
211+
}
212+
213+
private void collectBeanNamesForAnnotation(Set<String> names,
214+
ListableBeanFactory beanFactory, Class<? extends Annotation> annotationType,
215+
boolean considerHierarchy) {
216+
names.addAll(
217+
BeanTypeRegistry.get(beanFactory).getNamesForAnnotation(annotationType));
218+
if (considerHierarchy) {
219+
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
220+
.getParentBeanFactory();
221+
if (parent instanceof ListableBeanFactory) {
222+
collectBeanNamesForAnnotation(names, (ListableBeanFactory) parent,
223+
annotationType, considerHierarchy);
224+
}
228225
}
229226
}
230227

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@
1616

1717
package org.springframework.boot.autoconfigure.condition;
1818

19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
1924
import java.util.Date;
2025

2126
import org.junit.Test;
2227

28+
import org.springframework.beans.factory.FactoryBean;
2329
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2430
import org.springframework.beans.factory.support.RootBeanDefinition;
2531
import org.springframework.boot.test.util.EnvironmentTestUtils;
@@ -124,6 +130,15 @@ public void withPropertyPlaceholderClassName() throws Exception {
124130
this.context.refresh();
125131
}
126132

133+
@Test
134+
public void beanProducedByFactoryBeanIsConsideredWhenMatchingOnAnnotation() {
135+
this.context.register(FactoryBeanConfiguration.class,
136+
OnAnnotationWithFactoryBeanConfiguration.class);
137+
this.context.refresh();
138+
assertThat(this.context.containsBean("bar")).isTrue();
139+
assertThat(this.context.getBeansOfType(ExampleBean.class)).hasSize(1);
140+
}
141+
127142
@Configuration
128143
@ConditionalOnBean(name = "foo")
129144
protected static class OnBeanNameConfiguration {
@@ -220,6 +235,27 @@ protected static class WithPropertyPlaceholderClassName {
220235

221236
}
222237

238+
@Configuration
239+
static class FactoryBeanConfiguration {
240+
241+
@Bean
242+
public ExampleFactoryBean exampleBeanFactoryBean() {
243+
return new ExampleFactoryBean();
244+
}
245+
246+
}
247+
248+
@Configuration
249+
@ConditionalOnBean(annotation = TestAnnotation.class)
250+
static class OnAnnotationWithFactoryBeanConfiguration {
251+
252+
@Bean
253+
public String bar() {
254+
return "bar";
255+
}
256+
257+
}
258+
223259
protected static class WithPropertyPlaceholderClassNameRegistrar
224260
implements ImportBeanDefinitionRegistrar {
225261

@@ -233,4 +269,46 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
233269

234270
}
235271

272+
public static class ExampleFactoryBean implements FactoryBean<ExampleBean> {
273+
274+
@Override
275+
public ExampleBean getObject() throws Exception {
276+
return new ExampleBean("fromFactory");
277+
}
278+
279+
@Override
280+
public Class<?> getObjectType() {
281+
return ExampleBean.class;
282+
}
283+
284+
@Override
285+
public boolean isSingleton() {
286+
return false;
287+
}
288+
289+
}
290+
291+
@TestAnnotation
292+
public static class ExampleBean {
293+
294+
private String value;
295+
296+
public ExampleBean(String value) {
297+
this.value = value;
298+
}
299+
300+
@Override
301+
public String toString() {
302+
return this.value;
303+
}
304+
305+
}
306+
307+
@Target(ElementType.TYPE)
308+
@Retention(RetentionPolicy.RUNTIME)
309+
@Documented
310+
public @interface TestAnnotation {
311+
312+
}
313+
236314
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java

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

1717
package org.springframework.boot.autoconfigure.condition;
1818

19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
1924
import java.util.Date;
2025

2126
import org.junit.Test;
@@ -313,6 +318,15 @@ public void currentContextIsIgnoredWhenUsingAncestorsStrategy() {
313318
assertThat(child.getBeansOfType(ExampleBean.class)).hasSize(2);
314319
}
315320

321+
@Test
322+
public void beanProducedByFactoryBeanIsConsideredWhenMatchingOnAnnotation() {
323+
this.context.register(ConcreteFactoryBeanConfiguration.class,
324+
OnAnnotationWithFactoryBeanConfiguration.class);
325+
this.context.refresh();
326+
assertThat(this.context.containsBean("bar")).isFalse();
327+
assertThat(this.context.getBeansOfType(ExampleBean.class)).hasSize(1);
328+
}
329+
316330
@Configuration
317331
protected static class OnBeanInParentsConfiguration {
318332

@@ -540,6 +554,17 @@ public String bar() {
540554

541555
}
542556

557+
@Configuration
558+
@ConditionalOnMissingBean(annotation = TestAnnotation.class)
559+
protected static class OnAnnotationWithFactoryBeanConfiguration {
560+
561+
@Bean
562+
public String bar() {
563+
return "bar";
564+
}
565+
566+
}
567+
543568
@Configuration
544569
@EnableScheduling
545570
protected static class FooConfiguration {
@@ -594,6 +619,7 @@ public ExampleBean exampleBean2() {
594619

595620
}
596621

622+
@TestAnnotation
597623
public static class ExampleBean {
598624

599625
private String value;
@@ -663,4 +689,11 @@ public boolean isSingleton() {
663689

664690
}
665691

692+
@Target(ElementType.TYPE)
693+
@Retention(RetentionPolicy.RUNTIME)
694+
@Documented
695+
public @interface TestAnnotation {
696+
697+
}
698+
666699
}

0 commit comments

Comments
 (0)