All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.boot.autoconfigure.condition.BeanTypeRegistry Maven / Gradle / Ivy

There is a newer version: 3.2.4
Show newest version
/*
 * Copyright 2012-2015 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.apache.commons.logging.Log;
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;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
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.core.ResolvableType;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardMethodMetadata;
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 * @author Andy Wilkinson * @since 1.2.0 */ abstract class BeanTypeRegistry { private static final Log logger = LogFactory.getLog(BeanTypeRegistry.class); 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 { Method method = getFactoryMethod(beanFactory, definition); Class generic = ResolvableType.forMethodReturnType(method) .as(FactoryBean.class).resolveGeneric(); if ((generic == null || generic.equals(Object.class)) && definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) { generic = getTypeFromAttribute( definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE)); } return generic; } private Method getFactoryMethod(ConfigurableListableBeanFactory beanFactory, BeanDefinition definition) throws Exception { if (definition instanceof AnnotatedBeanDefinition) { MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) definition) .getFactoryMethodMetadata(); if (factoryMethodMetadata instanceof StandardMethodMetadata) { return ((StandardMethodMetadata) factoryMethodMetadata) .getIntrospectedMethod(); } } BeanDefinition factoryDefinition = beanFactory .getBeanDefinition(definition.getFactoryBeanName()); Class factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(), beanFactory.getBeanClassLoader()); return ReflectionUtils.findMethod(factoryClass, definition.getFactoryMethodName()); } 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 = getTypeFromAttribute( definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE)); } return generic; } private Class getTypeFromAttribute(Object attribute) throws ClassNotFoundException, LinkageError { if (attribute instanceof Class) { return (Class) attribute; } if (attribute instanceof String) { return ClassUtils.forName((String) attribute, null); } 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 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 SmartInitializingSingleton { private static final String BEAN_NAME = BeanTypeRegistry.class.getName(); private final DefaultListableBeanFactory beanFactory; private final Map> beanTypes = new HashMap>(); 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 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)) { 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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy