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.5
Show newest version
/*
 * Copyright 2012-2019 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
 *
 *      https://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.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

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.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.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
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 DefaultListableBeanFactory}. 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 */ 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 beanTypes = new HashMap<>(); private final Map beanDefinitions = new HashMap<>(); private BeanTypeRegistry(DefaultListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * Return the names of beans matching the given type (including subclasses), judging * from either bean definitions or the value of {@link FactoryBean#getObjectType()} in * the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not * cause early bean initialization. * @param type the class or interface to match (must not be {@code null}) * @param typeExtractor function used to extract the actual type * @return the names of beans (or objects created by FactoryBeans) matching the given * object type (including subclasses), or an empty set if none */ public Set getNamesForType(Class type, TypeExtractor typeExtractor) { updateTypesIfNecessary(); return this.beanTypes.entrySet().stream().filter((entry) -> { Class beanType = extractType(entry.getValue(), typeExtractor); return beanType != null && type.isAssignableFrom(beanType); }).map(Map.Entry::getKey).collect(Collectors.toCollection(LinkedHashSet::new)); } private Class extractType(ResolvableType type, TypeExtractor extractor) { return (type != null) ? extractor.getBeanType(type) : null; } /** * Returns the names of beans annotated with the given {@code annotation}, judging * from either bean definitions or the value of {@link FactoryBean#getObjectType()} in * the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not * cause early bean initialization. * @param annotation the annotation to match (must not be {@code null}) * @return the names of beans (or objects created by FactoryBeans) annotated with the * given annotation, or an empty set if none */ public Set getNamesForAnnotation(Class annotation) { updateTypesIfNecessary(); return this.beanTypes.entrySet().stream() .filter((entry) -> entry.getValue() != null && AnnotationUtils.findAnnotation(entry.getValue().resolve(), annotation) != null) .map(Map.Entry::getKey).collect(Collectors.toCollection(LinkedHashSet::new)); } @Override public void afterSingletonsInstantiated() { // We're done at this point, free up some memory this.beanTypes.clear(); this.beanDefinitions.clear(); } private void updateTypesIfNecessary() { this.beanFactory.getBeanNamesIterator().forEachRemaining(this::updateTypesIfNecessary); } private void updateTypesIfNecessary(String name) { if (!this.beanTypes.containsKey(name)) { addBeanType(name); } else { updateBeanType(name); } } private void addBeanType(String name) { if (this.beanFactory.containsSingleton(name)) { this.beanTypes.put(name, getType(name, null)); } else if (!this.beanFactory.isAlias(name)) { addBeanTypeForNonAliasDefinition(name); } } private void addBeanTypeForNonAliasDefinition(String name) { RootBeanDefinition definition = getBeanDefinition(name); if (definition != null) { addBeanTypeForNonAliasDefinition(name, definition); } } private void updateBeanType(String name) { if (this.beanFactory.isAlias(name) || this.beanFactory.containsSingleton(name)) { return; } RootBeanDefinition definition = getBeanDefinition(name); if (definition == null) { return; } RootBeanDefinition previous = this.beanDefinitions.put(name, definition); if (previous != null && !definition.equals(previous)) { addBeanTypeForNonAliasDefinition(name, definition); } } private RootBeanDefinition getBeanDefinition(String name) { try { return (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(name); } catch (BeanDefinitionStoreException ex) { logIgnoredError("unresolvable metadata in bean definition", name, ex); return null; } } private void addBeanTypeForNonAliasDefinition(String name, RootBeanDefinition definition) { try { if (!definition.isAbstract() && !requiresEagerInit(definition.getFactoryBeanName())) { ResolvableType factoryMethodReturnType = getFactoryMethodReturnType(definition); String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + name; if (this.beanFactory.isFactoryBean(factoryBeanName)) { ResolvableType factoryBeanGeneric = getFactoryBeanGeneric(this.beanFactory, definition, factoryMethodReturnType); this.beanTypes.put(name, factoryBeanGeneric); this.beanTypes.put(factoryBeanName, getType(factoryBeanName, factoryMethodReturnType)); } else { this.beanTypes.put(name, getType(name, factoryMethodReturnType)); } } this.beanDefinitions.put(name, definition); } catch (CannotLoadBeanClassException ex) { // Probably contains a placeholder logIgnoredError("bean class loading failure for bean", name, ex); } } private boolean requiresEagerInit(String factoryBeanName) { return (factoryBeanName != null && this.beanFactory.isFactoryBean(factoryBeanName) && !this.beanFactory.containsSingleton(factoryBeanName)); } private ResolvableType getFactoryMethodReturnType(BeanDefinition definition) { try { if (StringUtils.hasLength(definition.getFactoryBeanName()) && StringUtils.hasLength(definition.getFactoryMethodName())) { Method method = getFactoryMethod(this.beanFactory, definition); ResolvableType type = (method != null) ? ResolvableType.forMethodReturnType(method) : null; return type; } } catch (Exception ex) { } return null; } 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 getFactoryMethod(definition, factoryClass); } private Method getFactoryMethod(BeanDefinition definition, Class factoryClass) { Method uniqueMethod = null; for (Method candidate : getCandidateFactoryMethods(definition, factoryClass)) { if (candidate.getName().equals(definition.getFactoryMethodName())) { if (uniqueMethod == null) { uniqueMethod = candidate; } else if (!hasMatchingParameterTypes(candidate, uniqueMethod)) { return null; } } } return uniqueMethod; } private Method[] getCandidateFactoryMethods(BeanDefinition definition, Class factoryClass) { return (shouldConsiderNonPublicMethods(definition) ? ReflectionUtils.getAllDeclaredMethods(factoryClass) : factoryClass.getMethods()); } private boolean shouldConsiderNonPublicMethods(BeanDefinition definition) { return (definition instanceof AbstractBeanDefinition) && ((AbstractBeanDefinition) definition).isNonPublicAccessAllowed(); } private boolean hasMatchingParameterTypes(Method candidate, Method current) { return Arrays.equals(candidate.getParameterTypes(), current.getParameterTypes()); } private void logIgnoredError(String message, String name, Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Ignoring " + message + " '" + name + "'", ex); } } /** * 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 factoryMethodReturnType the factory method return type * @return the generic type of the {@link FactoryBean} or {@code null} */ private ResolvableType getFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory, BeanDefinition definition, ResolvableType factoryMethodReturnType) { try { if (factoryMethodReturnType != null) { return getFactoryBeanType(definition, factoryMethodReturnType); } if (StringUtils.hasLength(definition.getBeanClassName())) { return getDirectFactoryBeanGeneric(beanFactory, definition); } } catch (Exception ex) { } return null; } private ResolvableType getDirectFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory, BeanDefinition definition) throws ClassNotFoundException, LinkageError { Class factoryBeanClass = ClassUtils.forName(definition.getBeanClassName(), beanFactory.getBeanClassLoader()); return getFactoryBeanType(definition, ResolvableType.forClass(factoryBeanClass)); } private ResolvableType getFactoryBeanType(BeanDefinition definition, ResolvableType type) throws ClassNotFoundException, LinkageError { ResolvableType generic = type.as(FactoryBean.class).getGeneric(); if ((generic == null || generic.resolve().equals(Object.class)) && definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) { generic = getTypeFromAttribute(definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE)); } return generic; } private ResolvableType getTypeFromAttribute(Object attribute) throws ClassNotFoundException, LinkageError { if (attribute instanceof Class) { return ResolvableType.forClass((Class) attribute); } if (attribute instanceof String) { return ResolvableType.forClass(ClassUtils.forName((String) attribute, null)); } return null; } private ResolvableType getType(String name, ResolvableType factoryMethodReturnType) { if (factoryMethodReturnType != null && !factoryMethodReturnType.resolve(Object.class).equals(Object.class)) { return factoryMethodReturnType; } Class type = this.beanFactory.getType(name); return (type != null) ? ResolvableType.forClass(type) : 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 */ static BeanTypeRegistry get(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 definition = BeanDefinitionBuilder.genericBeanDefinition(BeanTypeRegistry.class, () -> new BeanTypeRegistry((DefaultListableBeanFactory) beanFactory)).getBeanDefinition(); listableBeanFactory.registerBeanDefinition(BEAN_NAME, definition); } return listableBeanFactory.getBean(BEAN_NAME, BeanTypeRegistry.class); } /** * Function used to extract the actual bean type from a source {@link ResolvableType}. * May be used to support parameterized containers for beans. */ @FunctionalInterface interface TypeExtractor { Class getBeanType(ResolvableType type); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy