mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Simplify BeanTypeRegistry by requiring a DefaultListableBeanFactory
Closes gh-8439
This commit is contained in:
parent
b443b745fb
commit
8a326a8713
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user