Simplify BeanTypeRegistry by requiring a DefaultListableBeanFactory

Closes gh-8439
This commit is contained in:
Andy Wilkinson 2017-03-01 14:36:26 +00:00
parent b443b745fb
commit 8a326a8713
2 changed files with 114 additions and 183 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-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.
@ -29,7 +29,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
@ -43,13 +42,14 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.ResolvableType;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* A registry of the bean types that are contained in a {@link ListableBeanFactory}.
* Provides similar functionality to
* A registry of the bean types that are contained in a
* {@link DefaultListableBeanFactory}. Provides similar functionality to
* {@link ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)} but is
* optimized for use by {@link OnBeanCondition} based on the following assumptions:
* <ul>
@ -62,12 +62,43 @@ import org.springframework.util.StringUtils;
* @author Andy Wilkinson
* @since 1.2.0
*/
abstract class BeanTypeRegistry {
final class BeanTypeRegistry implements SmartInitializingSingleton {
private static final Log logger = LogFactory.getLog(BeanTypeRegistry.class);
static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
private static final String BEAN_NAME = BeanTypeRegistry.class.getName();
private final DefaultListableBeanFactory beanFactory;
private final Map<String, Class<?>> beanTypes = new HashMap<String, Class<?>>();
private int lastBeanDefinitionCount = 0;
private BeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Factory method to get the {@link BeanTypeRegistry} for a given {@link BeanFactory}.
* @param beanFactory the source bean factory
* @return the {@link BeanTypeRegistry} for the given bean factory
*/
public static BeanTypeRegistry create(ListableBeanFactory beanFactory) {
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
Assert.isTrue(listableBeanFactory.isAllowEagerClassLoading(),
"Bean factory must allow eager class loading");
if (!listableBeanFactory.containsLocalBean(BEAN_NAME)) {
BeanDefinition bd = new RootBeanDefinition(BeanTypeRegistry.class);
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, beanFactory);
listableBeanFactory.registerBeanDefinition(BEAN_NAME, bd);
}
return listableBeanFactory.getBean(BEAN_NAME, BeanTypeRegistry.class);
}
/**
* Return the names of beans matching the given type (including subclasses), judging
* from either bean definitions or the value of {@code getObjectType} in the case of
@ -76,7 +107,81 @@ abstract class BeanTypeRegistry {
* @return the names of beans (or objects created by FactoryBeans) matching the given
* object type (including subclasses), or an empty set if none
*/
public abstract Set<String> getNamesForType(Class<?> type);
Set<String> getNamesForType(Class<?> type) {
if (this.lastBeanDefinitionCount != this.beanFactory.getBeanDefinitionCount()) {
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
while (names.hasNext()) {
String name = names.next();
if (!this.beanTypes.containsKey(name)) {
addBeanType(name);
}
}
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
}
Set<String> matches = new LinkedHashSet<String>();
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
matches.add(entry.getKey());
}
}
return matches;
}
@Override
public void afterSingletonsInstantiated() {
// We're done at this point, free up some memory
this.beanTypes.clear();
this.lastBeanDefinitionCount = 0;
}
private void addBeanType(String name) {
if (this.beanFactory.containsSingleton(name)) {
this.beanTypes.put(name, this.beanFactory.getType(name));
}
else if (!this.beanFactory.isAlias(name)) {
addBeanTypeForNonAliasDefinition(name);
}
}
private void addBeanTypeForNonAliasDefinition(String name) {
try {
String factoryName = BeanFactory.FACTORY_BEAN_PREFIX + name;
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
.getMergedBeanDefinition(name);
if (!beanDefinition.isAbstract()
&& !requiresEagerInit(beanDefinition.getFactoryBeanName())) {
if (this.beanFactory.isFactoryBean(factoryName)) {
Class<?> factoryBeanGeneric = getFactoryBeanGeneric(this.beanFactory,
beanDefinition, name);
this.beanTypes.put(name, factoryBeanGeneric);
this.beanTypes.put(factoryName,
this.beanFactory.getType(factoryName));
}
else {
this.beanTypes.put(name, this.beanFactory.getType(name));
}
}
}
catch (CannotLoadBeanClassException ex) {
// Probably contains a placeholder
logIgnoredError("bean class loading failure for bean", name, ex);
}
catch (BeanDefinitionStoreException ex) {
// Probably contains a placeholder
logIgnoredError("unresolvable metadata in bean definition", name, ex);
}
}
private void logIgnoredError(String message, String name, Exception ex) {
if (BeanTypeRegistry.logger.isDebugEnabled()) {
BeanTypeRegistry.logger.debug("Ignoring " + message + " '" + name + "'", ex);
}
}
private boolean requiresEagerInit(String factoryBeanName) {
return (factoryBeanName != null && this.beanFactory.isFactoryBean(factoryBeanName)
&& !this.beanFactory.containsSingleton(factoryBeanName));
}
/**
* Attempt to guess the type that a {@link FactoryBean} will return based on the
@ -86,9 +191,8 @@ abstract class BeanTypeRegistry {
* @param name the name of the bean
* @return the generic type of the {@link FactoryBean} or {@code null}
*/
protected final Class<?> getFactoryBeanGeneric(
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
String name) {
private Class<?> getFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory,
BeanDefinition definition, String name) {
try {
return doGetFactoryBeanGeneric(beanFactory, definition, name);
}
@ -198,177 +302,4 @@ abstract class BeanTypeRegistry {
return null;
}
/**
* Factory method to get the {@link BeanTypeRegistry} for a given {@link BeanFactory}.
* @param beanFactory the source bean factory
* @return the {@link BeanTypeRegistry} for the given bean factory
*/
public static BeanTypeRegistry get(ListableBeanFactory beanFactory) {
if (beanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
if (listableBeanFactory.isAllowEagerClassLoading()) {
return OptimizedBeanTypeRegistry.getFromFactory(listableBeanFactory);
}
}
return new DefaultBeanTypeRegistry(beanFactory);
}
/**
* Default (non-optimized) {@link BeanTypeRegistry} implementation.
*/
static class DefaultBeanTypeRegistry extends BeanTypeRegistry {
private final ListableBeanFactory beanFactory;
DefaultBeanTypeRegistry(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Set<String> getNamesForType(Class<?> type) {
Set<String> result = new LinkedHashSet<String>();
result.addAll(Arrays
.asList(this.beanFactory.getBeanNamesForType(type, true, false)));
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
collectBeanNamesForTypeFromFactoryBeans(result,
(ConfigurableListableBeanFactory) this.beanFactory, type);
}
return result;
}
private void collectBeanNamesForTypeFromFactoryBeans(Set<String> result,
ConfigurableListableBeanFactory beanFactory, Class<?> type) {
String[] names = beanFactory.getBeanNamesForType(FactoryBean.class, true,
false);
for (String name : names) {
name = BeanFactoryUtils.transformedBeanName(name);
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
Class<?> generic = getFactoryBeanGeneric(beanFactory, beanDefinition,
name);
if (generic != null && ClassUtils.isAssignable(type, generic)) {
result.add(name);
}
}
}
}
/**
* {@link BeanTypeRegistry} optimized for {@link DefaultListableBeanFactory}
* implementations that allow eager class loading.
*/
static class OptimizedBeanTypeRegistry extends BeanTypeRegistry
implements SmartInitializingSingleton {
private static final String BEAN_NAME = BeanTypeRegistry.class.getName();
private final DefaultListableBeanFactory beanFactory;
private final Map<String, Class<?>> beanTypes = new HashMap<String, Class<?>>();
private int lastBeanDefinitionCount = 0;
OptimizedBeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterSingletonsInstantiated() {
// We're done at this point, free up some memory
this.beanTypes.clear();
this.lastBeanDefinitionCount = 0;
}
@Override
public Set<String> getNamesForType(Class<?> type) {
if (this.lastBeanDefinitionCount != this.beanFactory
.getBeanDefinitionCount()) {
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
while (names.hasNext()) {
String name = names.next();
if (!this.beanTypes.containsKey(name)) {
addBeanType(name);
}
}
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
}
Set<String> matches = new LinkedHashSet<String>();
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
matches.add(entry.getKey());
}
}
return matches;
}
private void addBeanType(String name) {
if (this.beanFactory.containsSingleton(name)) {
this.beanTypes.put(name, this.beanFactory.getType(name));
}
else if (!this.beanFactory.isAlias(name)) {
addBeanTypeForNonAliasDefinition(name);
}
}
private void addBeanTypeForNonAliasDefinition(String name) {
try {
String factoryName = BeanFactory.FACTORY_BEAN_PREFIX + name;
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
.getMergedBeanDefinition(name);
if (!beanDefinition.isAbstract()
&& !requiresEagerInit(beanDefinition.getFactoryBeanName())) {
if (this.beanFactory.isFactoryBean(factoryName)) {
Class<?> factoryBeanGeneric = getFactoryBeanGeneric(
this.beanFactory, beanDefinition, name);
this.beanTypes.put(name, factoryBeanGeneric);
this.beanTypes.put(factoryName,
this.beanFactory.getType(factoryName));
}
else {
this.beanTypes.put(name, this.beanFactory.getType(name));
}
}
}
catch (CannotLoadBeanClassException ex) {
// Probably contains a placeholder
logIgnoredError("bean class loading failure for bean", name, ex);
}
catch (BeanDefinitionStoreException ex) {
// Probably contains a placeholder
logIgnoredError("unresolvable metadata in bean definition", name, ex);
}
}
private void logIgnoredError(String message, String name, Exception ex) {
if (BeanTypeRegistry.logger.isDebugEnabled()) {
BeanTypeRegistry.logger.debug("Ignoring " + message + " '" + name + "'",
ex);
}
}
private boolean requiresEagerInit(String factoryBeanName) {
return (factoryBeanName != null
&& this.beanFactory.isFactoryBean(factoryBeanName)
&& !this.beanFactory.containsSingleton(factoryBeanName));
}
/**
* Returns the {@link OptimizedBeanTypeRegistry} for the given bean factory.
* @param factory the source {@link BeanFactory}
* @return the {@link OptimizedBeanTypeRegistry}
*/
public static OptimizedBeanTypeRegistry getFromFactory(
DefaultListableBeanFactory factory) {
if (!factory.containsLocalBean(BEAN_NAME)) {
BeanDefinition bd = new RootBeanDefinition(
OptimizedBeanTypeRegistry.class);
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, factory);
factory.registerBeanDefinition(BEAN_NAME, bd);
}
return factory.getBean(BEAN_NAME, OptimizedBeanTypeRegistry.class);
}
}
}

View File

@ -184,7 +184,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
private void collectBeanNamesForType(Set<String> result,
ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
result.addAll(BeanTypeRegistry.create(beanFactory).getNamesForType(type));
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
.getParentBeanFactory();