001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.beans.factory.annotation; 018 019import java.lang.reflect.Method; 020 021import org.springframework.beans.BeansException; 022import org.springframework.beans.factory.BeanFactory; 023import org.springframework.beans.factory.BeanFactoryUtils; 024import org.springframework.beans.factory.NoSuchBeanDefinitionException; 025import org.springframework.beans.factory.NoUniqueBeanDefinitionException; 026import org.springframework.beans.factory.config.BeanDefinition; 027import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 028import org.springframework.beans.factory.support.AbstractBeanDefinition; 029import org.springframework.beans.factory.support.AutowireCandidateQualifier; 030import org.springframework.beans.factory.support.RootBeanDefinition; 031import org.springframework.core.annotation.AnnotationUtils; 032import org.springframework.util.Assert; 033import org.springframework.util.ObjectUtils; 034 035/** 036 * Convenience methods performing bean lookups related to annotations, for example 037 * Spring's {@link Qualifier @Qualifier} annotation. 038 * 039 * @author Juergen Hoeller 040 * @author Chris Beams 041 * @since 3.1.2 042 * @see BeanFactoryUtils 043 */ 044public abstract class BeanFactoryAnnotationUtils { 045 046 /** 047 * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a 048 * qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given 049 * qualifier, or having a bean name matching the given qualifier. 050 * @param beanFactory the BeanFactory to get the target bean from 051 * @param beanType the type of bean to retrieve 052 * @param qualifier the qualifier for selecting between multiple bean matches 053 * @return the matching bean of type {@code T} (never {@code null}) 054 * @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found 055 * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found 056 * @throws BeansException if the bean could not be created 057 * @see BeanFactory#getBean(Class) 058 */ 059 public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) 060 throws BeansException { 061 062 Assert.notNull(beanFactory, "BeanFactory must not be null"); 063 064 if (beanFactory instanceof ConfigurableListableBeanFactory) { 065 // Full qualifier matching supported. 066 return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier); 067 } 068 else if (beanFactory.containsBean(qualifier)) { 069 // Fallback: target bean at least found by bean name. 070 return beanFactory.getBean(qualifier, beanType); 071 } 072 else { 073 throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() + 074 " bean found for bean name '" + qualifier + 075 "'! (Note: Qualifier matching not supported because given " + 076 "BeanFactory does not implement ConfigurableListableBeanFactory.)"); 077 } 078 } 079 080 /** 081 * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a qualifier 082 * (e.g. {@code <qualifier>} or {@code @Qualifier}) matching the given qualifier). 083 * @param bf the BeanFactory to get the target bean from 084 * @param beanType the type of bean to retrieve 085 * @param qualifier the qualifier for selecting between multiple bean matches 086 * @return the matching bean of type {@code T} (never {@code null}) 087 */ 088 private static <T> T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class<T> beanType, String qualifier) { 089 String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType); 090 String matchingBean = null; 091 for (String beanName : candidateBeans) { 092 if (isQualifierMatch(qualifier, beanName, bf)) { 093 if (matchingBean != null) { 094 throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName); 095 } 096 matchingBean = beanName; 097 } 098 } 099 if (matchingBean != null) { 100 return bf.getBean(matchingBean, beanType); 101 } 102 else if (bf.containsBean(qualifier)) { 103 // Fallback: target bean at least found by bean name - probably a manually registered singleton. 104 return bf.getBean(qualifier, beanType); 105 } 106 else { 107 throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() + 108 " bean found for qualifier '" + qualifier + "' - neither qualifier match nor bean name match!"); 109 } 110 } 111 112 /** 113 * Check whether the named bean declares a qualifier of the given name. 114 * @param qualifier the qualifier to match 115 * @param beanName the name of the candidate bean 116 * @param bf the {@code BeanFactory} from which to retrieve the named bean 117 * @return {@code true} if either the bean definition (in the XML case) 118 * or the bean's factory method (in the {@code @Bean} case) defines a matching 119 * qualifier value (through {@code <qualifier>} or {@code @Qualifier}) 120 */ 121 private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) { 122 if (bf.containsBean(beanName)) { 123 try { 124 BeanDefinition bd = bf.getMergedBeanDefinition(beanName); 125 // Explicit qualifier metadata on bean definition? (typically in XML definition) 126 if (bd instanceof AbstractBeanDefinition) { 127 AbstractBeanDefinition abd = (AbstractBeanDefinition) bd; 128 AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName()); 129 if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) || 130 qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) { 131 return true; 132 } 133 } 134 // Corresponding qualifier on factory method? (typically in configuration class) 135 if (bd instanceof RootBeanDefinition) { 136 Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); 137 if (factoryMethod != null) { 138 Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod, Qualifier.class); 139 if (targetAnnotation != null) { 140 return qualifier.equals(targetAnnotation.value()); 141 } 142 } 143 } 144 // Corresponding qualifier on bean implementation class? (for custom user types) 145 Class<?> beanType = bf.getType(beanName); 146 if (beanType != null) { 147 Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class); 148 if (targetAnnotation != null) { 149 return qualifier.equals(targetAnnotation.value()); 150 } 151 } 152 } 153 catch (NoSuchBeanDefinitionException ex) { 154 // Ignore - can't compare qualifiers for a manually registered singleton object 155 } 156 } 157 return false; 158 } 159 160}