Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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);
}
}