|
|
|
@ -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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|