Improve OnBeanCondition Performance
Update OnBeanCondition to use a new BeanTypeRegistry which includes optimized code when using a DefaultListableBeanFactory. The optimized version calculates bean types only once per bean and caches the result. Prior to this change the sample "pet clinic" application would spend 400-500 milliseconds evaluating OnBeanConditions, after this change it spends around 120 milliseconds. Fixes gh-1803
This commit is contained in:
parent
d6f2f0deee
commit
45b579c439
|
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.condition;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
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
|
||||||
|
* {@link ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)} but is
|
||||||
|
* optimized for use by {@link OnBeanCondition} based on the following assumptions:
|
||||||
|
* <ul>
|
||||||
|
* <li>Bean definitions will not change type.</li>
|
||||||
|
* <li>Beans definitions will not be removed.</li>
|
||||||
|
* <li>Beans will not be created in parallel.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
abstract class BeanTypeRegistry {
|
||||||
|
|
||||||
|
public static long check;
|
||||||
|
|
||||||
|
static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* FactoryBeans. Will include singletons but not cause early bean initialization.
|
||||||
|
* @param type the class or interface to match (must not be {@code null})
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to guess the type that a {@link FactoryBean} will return based on the
|
||||||
|
* generics in its method signature.
|
||||||
|
* @param beanFactory the source bean factory
|
||||||
|
* @param definition the bean definition
|
||||||
|
* @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) {
|
||||||
|
try {
|
||||||
|
return doGetFactoryBeanGeneric(beanFactory, definition, name);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> doGetFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory,
|
||||||
|
BeanDefinition definition, String name) throws Exception,
|
||||||
|
ClassNotFoundException, LinkageError {
|
||||||
|
if (StringUtils.hasLength(definition.getFactoryBeanName())
|
||||||
|
&& StringUtils.hasLength(definition.getFactoryMethodName())) {
|
||||||
|
return getConfigurationClassFactoryBeanGeneric(beanFactory, definition, name);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasLength(definition.getBeanClassName())) {
|
||||||
|
return getDirectFactoryBeanGeneric(beanFactory, definition, name);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getConfigurationClassFactoryBeanGeneric(
|
||||||
|
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
|
||||||
|
String name) throws Exception {
|
||||||
|
BeanDefinition factoryDefinition = beanFactory.getBeanDefinition(definition
|
||||||
|
.getFactoryBeanName());
|
||||||
|
Class<?> factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(),
|
||||||
|
beanFactory.getBeanClassLoader());
|
||||||
|
Method method = ReflectionUtils.findMethod(factoryClass,
|
||||||
|
definition.getFactoryMethodName());
|
||||||
|
Class<?> generic = ResolvableType.forMethodReturnType(method)
|
||||||
|
.as(FactoryBean.class).resolveGeneric();
|
||||||
|
if ((generic == null || generic.equals(Object.class))
|
||||||
|
&& definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) {
|
||||||
|
generic = (Class<?>) definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE);
|
||||||
|
}
|
||||||
|
return generic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getDirectFactoryBeanGeneric(
|
||||||
|
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
|
||||||
|
String name) throws ClassNotFoundException, LinkageError {
|
||||||
|
Class<?> factoryBeanClass = ClassUtils.forName(definition.getBeanClassName(),
|
||||||
|
beanFactory.getBeanClassLoader());
|
||||||
|
Class<?> generic = ResolvableType.forClass(factoryBeanClass)
|
||||||
|
.as(FactoryBean.class).resolveGeneric();
|
||||||
|
if ((generic == null || generic.equals(Object.class))
|
||||||
|
&& definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) {
|
||||||
|
generic = (Class<?>) definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE);
|
||||||
|
}
|
||||||
|
return generic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public 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
|
||||||
|
ApplicationListener<ContextRefreshedEvent> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public OptimizedBeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
|
||||||
|
this.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||||
|
// 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)) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -27,18 +27,14 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
|
||||||
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Condition;
|
import org.springframework.context.annotation.Condition;
|
||||||
import org.springframework.context.annotation.ConditionContext;
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
import org.springframework.context.annotation.ConfigurationCondition;
|
import org.springframework.context.annotation.ConfigurationCondition;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.ResolvableType;
|
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
|
@ -57,15 +53,16 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Jakub Kubrynski
|
* @author Jakub Kubrynski
|
||||||
*/
|
*/
|
||||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||||
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {
|
public class OnBeanCondition extends SpringBootCondition implements
|
||||||
|
ConfigurationCondition {
|
||||||
|
|
||||||
|
private static final String[] NO_BEANS = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean definition attribute name for factory beans to signal their product type (if
|
* Bean definition attribute name for factory beans to signal their product type (if
|
||||||
* known and it can't be deduced from the factory bean class).
|
* known and it can't be deduced from the factory bean class).
|
||||||
*/
|
*/
|
||||||
public static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
|
public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE;
|
||||||
|
|
||||||
private static final String[] NO_BEANS = {};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigurationPhase getConfigurationPhase() {
|
public ConfigurationPhase getConfigurationPhase() {
|
||||||
|
|
@ -75,9 +72,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
@Override
|
@Override
|
||||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||||
AnnotatedTypeMetadata metadata) {
|
AnnotatedTypeMetadata metadata) {
|
||||||
|
|
||||||
StringBuffer matchMessage = new StringBuffer();
|
StringBuffer matchMessage = new StringBuffer();
|
||||||
|
|
||||||
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
|
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
|
||||||
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
|
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
|
||||||
ConditionalOnBean.class);
|
ConditionalOnBean.class);
|
||||||
|
|
@ -89,7 +84,6 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
matchMessage.append("@ConditionalOnBean " + spec + " found the following "
|
matchMessage.append("@ConditionalOnBean " + spec + " found the following "
|
||||||
+ matching);
|
+ matching);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
|
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
|
||||||
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
|
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
|
||||||
ConditionalOnMissingBean.class);
|
ConditionalOnMissingBean.class);
|
||||||
|
|
@ -101,12 +95,10 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
matchMessage.append(matchMessage.length() == 0 ? "" : " ");
|
matchMessage.append(matchMessage.length() == 0 ? "" : " ");
|
||||||
matchMessage.append("@ConditionalOnMissingBean " + spec + " found no beans");
|
matchMessage.append("@ConditionalOnMissingBean " + spec + " found no beans");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConditionOutcome.match(matchMessage.toString());
|
return ConditionOutcome.match(matchMessage.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
|
private List<String> getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
|
||||||
|
|
||||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||||
if (beans.getStrategy() == SearchStrategy.PARENTS) {
|
if (beans.getStrategy() == SearchStrategy.PARENTS) {
|
||||||
BeanFactory parent = beanFactory.getParentBeanFactory();
|
BeanFactory parent = beanFactory.getParentBeanFactory();
|
||||||
|
|
@ -145,9 +137,9 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
return beanFactory.containsLocalBean(beanName);
|
return beanFactory.containsLocalBean(beanName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<String> getBeanNamesForType(
|
private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory,
|
||||||
ConfigurableListableBeanFactory beanFactory, String type,
|
String type, ClassLoader classLoader, boolean considerHierarchy)
|
||||||
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
|
throws LinkageError {
|
||||||
try {
|
try {
|
||||||
Set<String> result = new LinkedHashSet<String>();
|
Set<String> result = new LinkedHashSet<String>();
|
||||||
collectBeanNamesForType(result, beanFactory,
|
collectBeanNamesForType(result, beanFactory,
|
||||||
|
|
@ -161,12 +153,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
|
|
||||||
private void collectBeanNamesForType(Set<String> result,
|
private void collectBeanNamesForType(Set<String> result,
|
||||||
ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
|
ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
|
||||||
// eagerInit set to false to prevent early instantiation
|
result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
|
||||||
result.addAll(Arrays.asList(beanFactory.getBeanNamesForType(type, true, false)));
|
|
||||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
|
||||||
collectBeanNamesForTypeFromFactoryBeans(result,
|
|
||||||
(ConfigurableListableBeanFactory) beanFactory, type);
|
|
||||||
}
|
|
||||||
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
|
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
|
||||||
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
|
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
|
||||||
.getParentBeanFactory();
|
.getParentBeanFactory();
|
||||||
|
|
@ -177,73 +164,6 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to collect bean names for type by considering FactoryBean generics. Some
|
|
||||||
* factory beans will not be able to determine their object type at this stage, so
|
|
||||||
* those are not eligible for matching this condition.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?> getFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory,
|
|
||||||
BeanDefinition definition, String name) {
|
|
||||||
try {
|
|
||||||
if (StringUtils.hasLength(definition.getFactoryBeanName())
|
|
||||||
&& StringUtils.hasLength(definition.getFactoryMethodName())) {
|
|
||||||
return getConfigurationClassFactoryBeanGeneric(beanFactory, definition,
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
if (StringUtils.hasLength(definition.getBeanClassName())) {
|
|
||||||
return getDirectFactoryBeanGeneric(beanFactory, definition, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?> getConfigurationClassFactoryBeanGeneric(
|
|
||||||
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
|
|
||||||
String name) throws Exception {
|
|
||||||
BeanDefinition factoryDefinition = beanFactory.getBeanDefinition(definition
|
|
||||||
.getFactoryBeanName());
|
|
||||||
Class<?> factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(),
|
|
||||||
beanFactory.getBeanClassLoader());
|
|
||||||
Method method = ReflectionUtils.findMethod(factoryClass,
|
|
||||||
definition.getFactoryMethodName());
|
|
||||||
Class<?> generic = ResolvableType.forMethodReturnType(method)
|
|
||||||
.as(FactoryBean.class).resolveGeneric();
|
|
||||||
if ((generic == null || generic.equals(Object.class))
|
|
||||||
&& definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) {
|
|
||||||
generic = (Class<?>) definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE);
|
|
||||||
}
|
|
||||||
return generic;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?> getDirectFactoryBeanGeneric(
|
|
||||||
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
|
|
||||||
String name) throws ClassNotFoundException, LinkageError {
|
|
||||||
Class<?> factoryBeanClass = ClassUtils.forName(definition.getBeanClassName(),
|
|
||||||
beanFactory.getBeanClassLoader());
|
|
||||||
Class<?> generic = ResolvableType.forClass(factoryBeanClass)
|
|
||||||
.as(FactoryBean.class).resolveGeneric();
|
|
||||||
if ((generic == null || generic.equals(Object.class))
|
|
||||||
&& definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) {
|
|
||||||
generic = (Class<?>) definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE);
|
|
||||||
}
|
|
||||||
return generic;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getBeanNamesForAnnotation(
|
private String[] getBeanNamesForAnnotation(
|
||||||
ConfigurableListableBeanFactory beanFactory, String type,
|
ConfigurableListableBeanFactory beanFactory, String type,
|
||||||
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
|
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue