org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.java

Source

/*
 * Copyright 2002-2018 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.beans.factory.annotation;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Predicate;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Convenience methods performing bean lookups related to Spring-specific annotations,
 * for example Spring's {@link Qualifier @Qualifier} annotation.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 3.1.2
 * @see BeanFactoryUtils
 */
public abstract class BeanFactoryAnnotationUtils {

    /**
     * Retrieve all bean of type {@code T} from the given {@code BeanFactory} declaring a
     * qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given
     * qualifier, or having a bean name matching the given qualifier.
     * @param beanFactory the factory to get the target beans from (also searching ancestors)
     * @param beanType the type of beans to retrieve
     * @param qualifier the qualifier for selecting among all type matches
     * @return the matching beans of type {@code T}
     * @throws BeansException if any of the matching beans could not be created
     * @since 5.1.1
     * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
     */
    public static <T> Map<String, T> qualifiedBeansOfType(ListableBeanFactory beanFactory, Class<T> beanType,
            String qualifier) throws BeansException {

        String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType);
        Map<String, T> result = new LinkedHashMap<>(4);
        for (String beanName : candidateBeans) {
            if (isQualifierMatch(qualifier::equals, beanName, beanFactory)) {
                result.put(beanName, beanFactory.getBean(beanName, beanType));
            }
        }
        return result;
    }

    /**
     * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
     * qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given
     * qualifier, or having a bean name matching the given qualifier.
     * @param beanFactory the factory to get the target bean from (also searching ancestors)
     * @param beanType the type of bean to retrieve
     * @param qualifier the qualifier for selecting between multiple bean matches
     * @return the matching bean of type {@code T} (never {@code null})
     * @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found
     * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
     * @throws BeansException if the bean could not be created
     * @see BeanFactoryUtils#beanOfTypeIncludingAncestors(ListableBeanFactory, Class)
     */
    public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier)
            throws BeansException {

        Assert.notNull(beanFactory, "BeanFactory must not be null");

        if (beanFactory instanceof ListableBeanFactory) {
            // Full qualifier matching supported.
            return qualifiedBeanOfType((ListableBeanFactory) beanFactory, beanType, qualifier);
        } else if (beanFactory.containsBean(qualifier)) {
            // Fallback: target bean at least found by bean name.
            return beanFactory.getBean(qualifier, beanType);
        } else {
            throw new NoSuchBeanDefinitionException(qualifier,
                    "No matching " + beanType.getSimpleName() + " bean found for bean name '" + qualifier
                            + "'! (Note: Qualifier matching not supported because given "
                            + "BeanFactory does not implement ConfigurableListableBeanFactory.)");
        }
    }

    /**
     * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a qualifier
     * (e.g. {@code <qualifier>} or {@code @Qualifier}) matching the given qualifier).
     * @param bf the factory to get the target bean from
     * @param beanType the type of bean to retrieve
     * @param qualifier the qualifier for selecting between multiple bean matches
     * @return the matching bean of type {@code T} (never {@code null})
     */
    private static <T> T qualifiedBeanOfType(ListableBeanFactory bf, Class<T> beanType, String qualifier) {
        String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType);
        String matchingBean = null;
        for (String beanName : candidateBeans) {
            if (isQualifierMatch(qualifier::equals, beanName, bf)) {
                if (matchingBean != null) {
                    throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName);
                }
                matchingBean = beanName;
            }
        }
        if (matchingBean != null) {
            return bf.getBean(matchingBean, beanType);
        } else if (bf.containsBean(qualifier)) {
            // Fallback: target bean at least found by bean name - probably a manually registered singleton.
            return bf.getBean(qualifier, beanType);
        } else {
            throw new NoSuchBeanDefinitionException(qualifier,
                    "No matching " + beanType.getSimpleName() + " bean found for qualifier '" + qualifier
                            + "' - neither qualifier match nor bean name match!");
        }
    }

    /**
     * Check whether the named bean declares a qualifier of the given name.
     * @param qualifier the qualifier to match
     * @param beanName the name of the candidate bean
     * @param beanFactory the factory from which to retrieve the named bean
     * @return {@code true} if either the bean definition (in the XML case)
     * or the bean's factory method (in the {@code @Bean} case) defines a matching
     * qualifier value (through {@code <qualifier>} or {@code @Qualifier})
     * @since 5.0
     */
    public static boolean isQualifierMatch(Predicate<String> qualifier, String beanName,
            @Nullable BeanFactory beanFactory) {

        // Try quick bean name or alias match first...
        if (qualifier.test(beanName)) {
            return true;
        }
        if (beanFactory != null) {
            for (String alias : beanFactory.getAliases(beanName)) {
                if (qualifier.test(alias)) {
                    return true;
                }
            }
            try {
                Class<?> beanType = beanFactory.getType(beanName);
                if (beanFactory instanceof ConfigurableBeanFactory) {
                    BeanDefinition bd = ((ConfigurableBeanFactory) beanFactory).getMergedBeanDefinition(beanName);
                    // Explicit qualifier metadata on bean definition? (typically in XML definition)
                    if (bd instanceof AbstractBeanDefinition) {
                        AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
                        AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
                        if (candidate != null) {
                            Object value = candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY);
                            if (value != null && qualifier.test(value.toString())) {
                                return true;
                            }
                        }
                    }
                    // Corresponding qualifier on factory method? (typically in configuration class)
                    if (bd instanceof RootBeanDefinition) {
                        Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
                        if (factoryMethod != null) {
                            Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod,
                                    Qualifier.class);
                            if (targetAnnotation != null) {
                                return qualifier.test(targetAnnotation.value());
                            }
                        }
                    }
                }
                // Corresponding qualifier on bean implementation class? (for custom user types)
                if (beanType != null) {
                    Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class);
                    if (targetAnnotation != null) {
                        return qualifier.test(targetAnnotation.value());
                    }
                }
            } catch (NoSuchBeanDefinitionException ex) {
                // Ignore - can't compare qualifiers for a manually registered singleton object
            }
        }
        return false;
    }

}