001/* 002 * Copyright 2002-2018 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; 020import java.util.LinkedHashMap; 021import java.util.Map; 022import java.util.function.Predicate; 023 024import org.springframework.beans.BeansException; 025import org.springframework.beans.factory.BeanFactory; 026import org.springframework.beans.factory.BeanFactoryUtils; 027import org.springframework.beans.factory.ListableBeanFactory; 028import org.springframework.beans.factory.NoSuchBeanDefinitionException; 029import org.springframework.beans.factory.NoUniqueBeanDefinitionException; 030import org.springframework.beans.factory.config.BeanDefinition; 031import org.springframework.beans.factory.config.ConfigurableBeanFactory; 032import org.springframework.beans.factory.support.AbstractBeanDefinition; 033import org.springframework.beans.factory.support.AutowireCandidateQualifier; 034import org.springframework.beans.factory.support.RootBeanDefinition; 035import org.springframework.core.annotation.AnnotationUtils; 036import org.springframework.lang.Nullable; 037import org.springframework.util.Assert; 038 039/** 040 * Convenience methods performing bean lookups related to Spring-specific annotations, 041 * for example Spring's {@link Qualifier @Qualifier} annotation. 042 * 043 * @author Juergen Hoeller 044 * @author Chris Beams 045 * @since 3.1.2 046 * @see BeanFactoryUtils 047 */ 048public abstract class BeanFactoryAnnotationUtils { 049 050 /** 051 * Retrieve all bean of type {@code T} from the given {@code BeanFactory} declaring a 052 * qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given 053 * qualifier, or having a bean name matching the given qualifier. 054 * @param beanFactory the factory to get the target beans from (also searching ancestors) 055 * @param beanType the type of beans to retrieve 056 * @param qualifier the qualifier for selecting among all type matches 057 * @return the matching beans of type {@code T} 058 * @throws BeansException if any of the matching beans could not be created 059 * @since 5.1.1 060 * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class) 061 */ 062 public static <T> Map<String, T> qualifiedBeansOfType( 063 ListableBeanFactory beanFactory, Class<T> beanType, String qualifier) throws BeansException { 064 065 String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType); 066 Map<String, T> result = new LinkedHashMap<>(4); 067 for (String beanName : candidateBeans) { 068 if (isQualifierMatch(qualifier::equals, beanName, beanFactory)) { 069 result.put(beanName, beanFactory.getBean(beanName, beanType)); 070 } 071 } 072 return result; 073 } 074 075 /** 076 * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a 077 * qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given 078 * qualifier, or having a bean name matching the given qualifier. 079 * @param beanFactory the factory to get the target bean from (also searching ancestors) 080 * @param beanType the type of bean to retrieve 081 * @param qualifier the qualifier for selecting between multiple bean matches 082 * @return the matching bean of type {@code T} (never {@code null}) 083 * @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found 084 * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found 085 * @throws BeansException if the bean could not be created 086 * @see BeanFactoryUtils#beanOfTypeIncludingAncestors(ListableBeanFactory, Class) 087 */ 088 public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) 089 throws BeansException { 090 091 Assert.notNull(beanFactory, "BeanFactory must not be null"); 092 093 if (beanFactory instanceof ListableBeanFactory) { 094 // Full qualifier matching supported. 095 return qualifiedBeanOfType((ListableBeanFactory) beanFactory, beanType, qualifier); 096 } 097 else if (beanFactory.containsBean(qualifier)) { 098 // Fallback: target bean at least found by bean name. 099 return beanFactory.getBean(qualifier, beanType); 100 } 101 else { 102 throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() + 103 " bean found for bean name '" + qualifier + 104 "'! (Note: Qualifier matching not supported because given " + 105 "BeanFactory does not implement ConfigurableListableBeanFactory.)"); 106 } 107 } 108 109 /** 110 * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a qualifier 111 * (e.g. {@code <qualifier>} or {@code @Qualifier}) matching the given qualifier). 112 * @param bf the factory to get the target bean from 113 * @param beanType the type of bean to retrieve 114 * @param qualifier the qualifier for selecting between multiple bean matches 115 * @return the matching bean of type {@code T} (never {@code null}) 116 */ 117 private static <T> T qualifiedBeanOfType(ListableBeanFactory bf, Class<T> beanType, String qualifier) { 118 String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType); 119 String matchingBean = null; 120 for (String beanName : candidateBeans) { 121 if (isQualifierMatch(qualifier::equals, beanName, bf)) { 122 if (matchingBean != null) { 123 throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName); 124 } 125 matchingBean = beanName; 126 } 127 } 128 if (matchingBean != null) { 129 return bf.getBean(matchingBean, beanType); 130 } 131 else if (bf.containsBean(qualifier)) { 132 // Fallback: target bean at least found by bean name - probably a manually registered singleton. 133 return bf.getBean(qualifier, beanType); 134 } 135 else { 136 throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() + 137 " bean found for qualifier '" + qualifier + "' - neither qualifier match nor bean name match!"); 138 } 139 } 140 141 /** 142 * Check whether the named bean declares a qualifier of the given name. 143 * @param qualifier the qualifier to match 144 * @param beanName the name of the candidate bean 145 * @param beanFactory the factory from which to retrieve the named bean 146 * @return {@code true} if either the bean definition (in the XML case) 147 * or the bean's factory method (in the {@code @Bean} case) defines a matching 148 * qualifier value (through {@code <qualifier>} or {@code @Qualifier}) 149 * @since 5.0 150 */ 151 public static boolean isQualifierMatch( 152 Predicate<String> qualifier, String beanName, @Nullable BeanFactory beanFactory) { 153 154 // Try quick bean name or alias match first... 155 if (qualifier.test(beanName)) { 156 return true; 157 } 158 if (beanFactory != null) { 159 for (String alias : beanFactory.getAliases(beanName)) { 160 if (qualifier.test(alias)) { 161 return true; 162 } 163 } 164 try { 165 Class<?> beanType = beanFactory.getType(beanName); 166 if (beanFactory instanceof ConfigurableBeanFactory) { 167 BeanDefinition bd = ((ConfigurableBeanFactory) beanFactory).getMergedBeanDefinition(beanName); 168 // Explicit qualifier metadata on bean definition? (typically in XML definition) 169 if (bd instanceof AbstractBeanDefinition) { 170 AbstractBeanDefinition abd = (AbstractBeanDefinition) bd; 171 AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName()); 172 if (candidate != null) { 173 Object value = candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY); 174 if (value != null && qualifier.test(value.toString())) { 175 return true; 176 } 177 } 178 } 179 // Corresponding qualifier on factory method? (typically in configuration class) 180 if (bd instanceof RootBeanDefinition) { 181 Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); 182 if (factoryMethod != null) { 183 Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod, Qualifier.class); 184 if (targetAnnotation != null) { 185 return qualifier.test(targetAnnotation.value()); 186 } 187 } 188 } 189 } 190 // Corresponding qualifier on bean implementation class? (for custom user types) 191 if (beanType != null) { 192 Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class); 193 if (targetAnnotation != null) { 194 return qualifier.test(targetAnnotation.value()); 195 } 196 } 197 } 198 catch (NoSuchBeanDefinitionException ex) { 199 // Ignore - can't compare qualifiers for a manually registered singleton object 200 } 201 } 202 return false; 203 } 204 205}