Skip to content

Commit 8a326a8

Browse files
committed
Simplify BeanTypeRegistry by requiring a DefaultListableBeanFactory
Closes gh-8439
1 parent b443b74 commit 8a326a8

File tree

2 files changed

+114
-183
lines changed

2 files changed

+114
-183
lines changed
Lines changed: 113 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-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.
@@ -29,7 +29,6 @@
2929

3030
import org.springframework.beans.factory.BeanDefinitionStoreException;
3131
import org.springframework.beans.factory.BeanFactory;
32-
import org.springframework.beans.factory.BeanFactoryUtils;
3332
import org.springframework.beans.factory.CannotLoadBeanClassException;
3433
import org.springframework.beans.factory.FactoryBean;
3534
import org.springframework.beans.factory.ListableBeanFactory;
@@ -43,13 +42,14 @@
4342
import org.springframework.core.ResolvableType;
4443
import org.springframework.core.type.MethodMetadata;
4544
import org.springframework.core.type.StandardMethodMetadata;
45+
import org.springframework.util.Assert;
4646
import org.springframework.util.ClassUtils;
4747
import org.springframework.util.ReflectionUtils;
4848
import org.springframework.util.StringUtils;
4949

5050
/**
51-
* A registry of the bean types that are contained in a {@link ListableBeanFactory}.
52-
* Provides similar functionality to
51+
* A registry of the bean types that are contained in a
52+
* {@link DefaultListableBeanFactory}. Provides similar functionality to
5353
* {@link ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)} but is
5454
* optimized for use by {@link OnBeanCondition} based on the following assumptions:
5555
* <ul>
@@ -62,12 +62,43 @@
6262
* @author Andy Wilkinson
6363
* @since 1.2.0
6464
*/
65-
abstract class BeanTypeRegistry {
65+
final class BeanTypeRegistry implements SmartInitializingSingleton {
6666

6767
private static final Log logger = LogFactory.getLog(BeanTypeRegistry.class);
6868

6969
static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
7070

71+
private static final String BEAN_NAME = BeanTypeRegistry.class.getName();
72+
73+
private final DefaultListableBeanFactory beanFactory;
74+
75+
private final Map<String, Class<?>> beanTypes = new HashMap<String, Class<?>>();
76+
77+
private int lastBeanDefinitionCount = 0;
78+
79+
private BeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
80+
this.beanFactory = beanFactory;
81+
}
82+
83+
/**
84+
* Factory method to get the {@link BeanTypeRegistry} for a given {@link BeanFactory}.
85+
* @param beanFactory the source bean factory
86+
* @return the {@link BeanTypeRegistry} for the given bean factory
87+
*/
88+
public static BeanTypeRegistry create(ListableBeanFactory beanFactory) {
89+
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
90+
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
91+
Assert.isTrue(listableBeanFactory.isAllowEagerClassLoading(),
92+
"Bean factory must allow eager class loading");
93+
if (!listableBeanFactory.containsLocalBean(BEAN_NAME)) {
94+
BeanDefinition bd = new RootBeanDefinition(BeanTypeRegistry.class);
95+
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, beanFactory);
96+
listableBeanFactory.registerBeanDefinition(BEAN_NAME, bd);
97+
98+
}
99+
return listableBeanFactory.getBean(BEAN_NAME, BeanTypeRegistry.class);
100+
}
101+
71102
/**
72103
* Return the names of beans matching the given type (including subclasses), judging
73104
* from either bean definitions or the value of {@code getObjectType} in the case of
@@ -76,7 +107,81 @@ abstract class BeanTypeRegistry {
76107
* @return the names of beans (or objects created by FactoryBeans) matching the given
77108
* object type (including subclasses), or an empty set if none
78109
*/
79-
public abstract Set<String> getNamesForType(Class<?> type);
110+
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+
}
118+
}
119+
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
120+
}
121+
Set<String> matches = new LinkedHashSet<String>();
122+
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
123+
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
124+
matches.add(entry.getKey());
125+
}
126+
}
127+
return matches;
128+
}
129+
130+
@Override
131+
public void afterSingletonsInstantiated() {
132+
// We're done at this point, free up some memory
133+
this.beanTypes.clear();
134+
this.lastBeanDefinitionCount = 0;
135+
}
136+
137+
private void addBeanType(String name) {
138+
if (this.beanFactory.containsSingleton(name)) {
139+
this.beanTypes.put(name, this.beanFactory.getType(name));
140+
}
141+
else if (!this.beanFactory.isAlias(name)) {
142+
addBeanTypeForNonAliasDefinition(name);
143+
}
144+
}
145+
146+
private void addBeanTypeForNonAliasDefinition(String name) {
147+
try {
148+
String factoryName = BeanFactory.FACTORY_BEAN_PREFIX + name;
149+
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
150+
.getMergedBeanDefinition(name);
151+
if (!beanDefinition.isAbstract()
152+
&& !requiresEagerInit(beanDefinition.getFactoryBeanName())) {
153+
if (this.beanFactory.isFactoryBean(factoryName)) {
154+
Class<?> factoryBeanGeneric = getFactoryBeanGeneric(this.beanFactory,
155+
beanDefinition, name);
156+
this.beanTypes.put(name, factoryBeanGeneric);
157+
this.beanTypes.put(factoryName,
158+
this.beanFactory.getType(factoryName));
159+
}
160+
else {
161+
this.beanTypes.put(name, this.beanFactory.getType(name));
162+
}
163+
}
164+
}
165+
catch (CannotLoadBeanClassException ex) {
166+
// Probably contains a placeholder
167+
logIgnoredError("bean class loading failure for bean", name, ex);
168+
}
169+
catch (BeanDefinitionStoreException ex) {
170+
// Probably contains a placeholder
171+
logIgnoredError("unresolvable metadata in bean definition", name, ex);
172+
}
173+
}
174+
175+
private void logIgnoredError(String message, String name, Exception ex) {
176+
if (BeanTypeRegistry.logger.isDebugEnabled()) {
177+
BeanTypeRegistry.logger.debug("Ignoring " + message + " '" + name + "'", ex);
178+
}
179+
}
180+
181+
private boolean requiresEagerInit(String factoryBeanName) {
182+
return (factoryBeanName != null && this.beanFactory.isFactoryBean(factoryBeanName)
183+
&& !this.beanFactory.containsSingleton(factoryBeanName));
184+
}
80185

81186
/**
82187
* Attempt to guess the type that a {@link FactoryBean} will return based on the
@@ -86,9 +191,8 @@ abstract class BeanTypeRegistry {
86191
* @param name the name of the bean
87192
* @return the generic type of the {@link FactoryBean} or {@code null}
88193
*/
89-
protected final Class<?> getFactoryBeanGeneric(
90-
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
91-
String name) {
194+
private Class<?> getFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory,
195+
BeanDefinition definition, String name) {
92196
try {
93197
return doGetFactoryBeanGeneric(beanFactory, definition, name);
94198
}
@@ -198,177 +302,4 @@ private Class<?> getTypeFromAttribute(Object attribute)
198302
return null;
199303
}
200304

201-
/**
202-
* Factory method to get the {@link BeanTypeRegistry} for a given {@link BeanFactory}.
203-
* @param beanFactory the source bean factory
204-
* @return the {@link BeanTypeRegistry} for the given bean factory
205-
*/
206-
public static BeanTypeRegistry get(ListableBeanFactory beanFactory) {
207-
if (beanFactory instanceof DefaultListableBeanFactory) {
208-
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
209-
if (listableBeanFactory.isAllowEagerClassLoading()) {
210-
return OptimizedBeanTypeRegistry.getFromFactory(listableBeanFactory);
211-
}
212-
}
213-
return new DefaultBeanTypeRegistry(beanFactory);
214-
}
215-
216-
/**
217-
* Default (non-optimized) {@link BeanTypeRegistry} implementation.
218-
*/
219-
static class DefaultBeanTypeRegistry extends BeanTypeRegistry {
220-
221-
private final ListableBeanFactory beanFactory;
222-
223-
DefaultBeanTypeRegistry(ListableBeanFactory beanFactory) {
224-
this.beanFactory = beanFactory;
225-
}
226-
227-
@Override
228-
public Set<String> getNamesForType(Class<?> type) {
229-
Set<String> result = new LinkedHashSet<String>();
230-
result.addAll(Arrays
231-
.asList(this.beanFactory.getBeanNamesForType(type, true, false)));
232-
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
233-
collectBeanNamesForTypeFromFactoryBeans(result,
234-
(ConfigurableListableBeanFactory) this.beanFactory, type);
235-
}
236-
return result;
237-
}
238-
239-
private void collectBeanNamesForTypeFromFactoryBeans(Set<String> result,
240-
ConfigurableListableBeanFactory beanFactory, Class<?> type) {
241-
String[] names = beanFactory.getBeanNamesForType(FactoryBean.class, true,
242-
false);
243-
for (String name : names) {
244-
name = BeanFactoryUtils.transformedBeanName(name);
245-
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
246-
Class<?> generic = getFactoryBeanGeneric(beanFactory, beanDefinition,
247-
name);
248-
if (generic != null && ClassUtils.isAssignable(type, generic)) {
249-
result.add(name);
250-
}
251-
}
252-
}
253-
254-
}
255-
256-
/**
257-
* {@link BeanTypeRegistry} optimized for {@link DefaultListableBeanFactory}
258-
* implementations that allow eager class loading.
259-
*/
260-
static class OptimizedBeanTypeRegistry extends BeanTypeRegistry
261-
implements SmartInitializingSingleton {
262-
263-
private static final String BEAN_NAME = BeanTypeRegistry.class.getName();
264-
265-
private final DefaultListableBeanFactory beanFactory;
266-
267-
private final Map<String, Class<?>> beanTypes = new HashMap<String, Class<?>>();
268-
269-
private int lastBeanDefinitionCount = 0;
270-
271-
OptimizedBeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
272-
this.beanFactory = beanFactory;
273-
}
274-
275-
@Override
276-
public void afterSingletonsInstantiated() {
277-
// We're done at this point, free up some memory
278-
this.beanTypes.clear();
279-
this.lastBeanDefinitionCount = 0;
280-
}
281-
282-
@Override
283-
public Set<String> getNamesForType(Class<?> type) {
284-
if (this.lastBeanDefinitionCount != this.beanFactory
285-
.getBeanDefinitionCount()) {
286-
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
287-
while (names.hasNext()) {
288-
String name = names.next();
289-
if (!this.beanTypes.containsKey(name)) {
290-
addBeanType(name);
291-
}
292-
}
293-
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
294-
}
295-
Set<String> matches = new LinkedHashSet<String>();
296-
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
297-
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
298-
matches.add(entry.getKey());
299-
}
300-
}
301-
return matches;
302-
}
303-
304-
private void addBeanType(String name) {
305-
if (this.beanFactory.containsSingleton(name)) {
306-
this.beanTypes.put(name, this.beanFactory.getType(name));
307-
}
308-
else if (!this.beanFactory.isAlias(name)) {
309-
addBeanTypeForNonAliasDefinition(name);
310-
}
311-
}
312-
313-
private void addBeanTypeForNonAliasDefinition(String name) {
314-
try {
315-
String factoryName = BeanFactory.FACTORY_BEAN_PREFIX + name;
316-
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
317-
.getMergedBeanDefinition(name);
318-
if (!beanDefinition.isAbstract()
319-
&& !requiresEagerInit(beanDefinition.getFactoryBeanName())) {
320-
if (this.beanFactory.isFactoryBean(factoryName)) {
321-
Class<?> factoryBeanGeneric = getFactoryBeanGeneric(
322-
this.beanFactory, beanDefinition, name);
323-
this.beanTypes.put(name, factoryBeanGeneric);
324-
this.beanTypes.put(factoryName,
325-
this.beanFactory.getType(factoryName));
326-
}
327-
else {
328-
this.beanTypes.put(name, this.beanFactory.getType(name));
329-
}
330-
}
331-
}
332-
catch (CannotLoadBeanClassException ex) {
333-
// Probably contains a placeholder
334-
logIgnoredError("bean class loading failure for bean", name, ex);
335-
}
336-
catch (BeanDefinitionStoreException ex) {
337-
// Probably contains a placeholder
338-
logIgnoredError("unresolvable metadata in bean definition", name, ex);
339-
}
340-
}
341-
342-
private void logIgnoredError(String message, String name, Exception ex) {
343-
if (BeanTypeRegistry.logger.isDebugEnabled()) {
344-
BeanTypeRegistry.logger.debug("Ignoring " + message + " '" + name + "'",
345-
ex);
346-
}
347-
}
348-
349-
private boolean requiresEagerInit(String factoryBeanName) {
350-
return (factoryBeanName != null
351-
&& this.beanFactory.isFactoryBean(factoryBeanName)
352-
&& !this.beanFactory.containsSingleton(factoryBeanName));
353-
}
354-
355-
/**
356-
* Returns the {@link OptimizedBeanTypeRegistry} for the given bean factory.
357-
* @param factory the source {@link BeanFactory}
358-
* @return the {@link OptimizedBeanTypeRegistry}
359-
*/
360-
public static OptimizedBeanTypeRegistry getFromFactory(
361-
DefaultListableBeanFactory factory) {
362-
if (!factory.containsLocalBean(BEAN_NAME)) {
363-
BeanDefinition bd = new RootBeanDefinition(
364-
OptimizedBeanTypeRegistry.class);
365-
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, factory);
366-
factory.registerBeanDefinition(BEAN_NAME, bd);
367-
368-
}
369-
return factory.getBean(BEAN_NAME, OptimizedBeanTypeRegistry.class);
370-
}
371-
372-
}
373-
374305
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory,
184184

185185
private void collectBeanNamesForType(Set<String> result,
186186
ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
187-
result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
187+
result.addAll(BeanTypeRegistry.create(beanFactory).getNamesForType(type));
188188
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
189189
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
190190
.getParentBeanFactory();

0 commit comments

Comments
 (0)