diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java
new file mode 100644
index 00000000000..5ba4027c971
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java
@@ -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:
+ *
+ * - Bean definitions will not change type.
+ * - Beans definitions will not be removed.
+ * - Beans will not be created in parallel.
+ *
+ *
+ * @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 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 getNamesForType(Class> type) {
+ Set result = new LinkedHashSet();
+ 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 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 {
+
+ private static final String BEAN_NAME = BeanTypeRegistry.class.getName();
+
+ private final DefaultListableBeanFactory beanFactory;
+
+ private final Map> beanTypes = new HashMap>();
+
+ 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 getNamesForType(Class> type) {
+ if (this.lastBeanDefinitionCount != this.beanFactory.getBeanDefinitionCount()) {
+ Iterator names = this.beanFactory.getBeanNamesIterator();
+ while (names.hasNext()) {
+ String name = names.next();
+ if (!this.beanTypes.containsKey(name)) {
+ addBeanType(name);
+ }
+ }
+ this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
+ }
+ Set matches = new LinkedHashSet();
+ for (Map.Entry> 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);
+ }
+
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java
index ed70312a148..f7213429507 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java
@@ -27,18 +27,14 @@ import java.util.List;
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.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
-import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.Ordered;
-import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.MethodMetadata;
@@ -57,15 +53,16 @@ import org.springframework.util.StringUtils;
* @author Jakub Kubrynski
*/
@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
* known and it can't be deduced from the factory bean class).
*/
- public static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
-
- private static final String[] NO_BEANS = {};
+ public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE;
@Override
public ConfigurationPhase getConfigurationPhase() {
@@ -75,9 +72,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
-
StringBuffer matchMessage = new StringBuffer();
-
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
ConditionalOnBean.class);
@@ -89,7 +84,6 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
matchMessage.append("@ConditionalOnBean " + spec + " found the following "
+ matching);
}
-
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
ConditionalOnMissingBean.class);
@@ -101,12 +95,10 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
matchMessage.append(matchMessage.length() == 0 ? "" : " ");
matchMessage.append("@ConditionalOnMissingBean " + spec + " found no beans");
}
-
return ConditionOutcome.match(matchMessage.toString());
}
private List getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
-
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beans.getStrategy() == SearchStrategy.PARENTS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
@@ -145,9 +137,9 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
return beanFactory.containsLocalBean(beanName);
}
- private Collection getBeanNamesForType(
- ConfigurableListableBeanFactory beanFactory, String type,
- ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
+ private Collection getBeanNamesForType(ListableBeanFactory beanFactory,
+ String type, ClassLoader classLoader, boolean considerHierarchy)
+ throws LinkageError {
try {
Set result = new LinkedHashSet();
collectBeanNamesForType(result, beanFactory,
@@ -161,12 +153,7 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
private void collectBeanNamesForType(Set result,
ListableBeanFactory beanFactory, Class> type, boolean considerHierarchy) {
- // eagerInit set to false to prevent early instantiation
- result.addAll(Arrays.asList(beanFactory.getBeanNamesForType(type, true, false)));
- if (beanFactory instanceof ConfigurableListableBeanFactory) {
- collectBeanNamesForTypeFromFactoryBeans(result,
- (ConfigurableListableBeanFactory) beanFactory, type);
- }
+ result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory)
.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 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(
ConfigurableListableBeanFactory beanFactory, String type,
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {