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.annotation.Annotation; 020import java.lang.reflect.AnnotatedElement; 021import java.lang.reflect.Method; 022import java.util.LinkedHashSet; 023import java.util.Map; 024import java.util.Set; 025 026import org.springframework.beans.SimpleTypeConverter; 027import org.springframework.beans.TypeConverter; 028import org.springframework.beans.factory.NoSuchBeanDefinitionException; 029import org.springframework.beans.factory.config.BeanDefinitionHolder; 030import org.springframework.beans.factory.config.DependencyDescriptor; 031import org.springframework.beans.factory.support.AutowireCandidateQualifier; 032import org.springframework.beans.factory.support.AutowireCandidateResolver; 033import org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver; 034import org.springframework.beans.factory.support.RootBeanDefinition; 035import org.springframework.core.MethodParameter; 036import org.springframework.core.annotation.AnnotatedElementUtils; 037import org.springframework.core.annotation.AnnotationAttributes; 038import org.springframework.core.annotation.AnnotationUtils; 039import org.springframework.lang.Nullable; 040import org.springframework.util.Assert; 041import org.springframework.util.ClassUtils; 042import org.springframework.util.ObjectUtils; 043import org.springframework.util.StringUtils; 044 045/** 046 * {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers 047 * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired. 048 * Also supports suggested expression values through a {@link Value value} annotation. 049 * 050 * <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available. 051 * 052 * @author Mark Fisher 053 * @author Juergen Hoeller 054 * @author Stephane Nicoll 055 * @since 2.5 056 * @see AutowireCandidateQualifier 057 * @see Qualifier 058 * @see Value 059 */ 060public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver { 061 062 private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2); 063 064 private Class<? extends Annotation> valueAnnotationType = Value.class; 065 066 067 /** 068 * Create a new QualifierAnnotationAutowireCandidateResolver 069 * for Spring's standard {@link Qualifier} annotation. 070 * <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available. 071 */ 072 @SuppressWarnings("unchecked") 073 public QualifierAnnotationAutowireCandidateResolver() { 074 this.qualifierTypes.add(Qualifier.class); 075 try { 076 this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier", 077 QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); 078 } 079 catch (ClassNotFoundException ex) { 080 // JSR-330 API not available - simply skip. 081 } 082 } 083 084 /** 085 * Create a new QualifierAnnotationAutowireCandidateResolver 086 * for the given qualifier annotation type. 087 * @param qualifierType the qualifier annotation to look for 088 */ 089 public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) { 090 Assert.notNull(qualifierType, "'qualifierType' must not be null"); 091 this.qualifierTypes.add(qualifierType); 092 } 093 094 /** 095 * Create a new QualifierAnnotationAutowireCandidateResolver 096 * for the given qualifier annotation types. 097 * @param qualifierTypes the qualifier annotations to look for 098 */ 099 public QualifierAnnotationAutowireCandidateResolver(Set<Class<? extends Annotation>> qualifierTypes) { 100 Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null"); 101 this.qualifierTypes.addAll(qualifierTypes); 102 } 103 104 105 /** 106 * Register the given type to be used as a qualifier when autowiring. 107 * <p>This identifies qualifier annotations for direct use (on fields, 108 * method parameters and constructor parameters) as well as meta 109 * annotations that in turn identify actual qualifier annotations. 110 * <p>This implementation only supports annotations as qualifier types. 111 * The default is Spring's {@link Qualifier} annotation which serves 112 * as a qualifier for direct use and also as a meta annotation. 113 * @param qualifierType the annotation type to register 114 */ 115 public void addQualifierType(Class<? extends Annotation> qualifierType) { 116 this.qualifierTypes.add(qualifierType); 117 } 118 119 /** 120 * Set the 'value' annotation type, to be used on fields, method parameters 121 * and constructor parameters. 122 * <p>The default value annotation type is the Spring-provided 123 * {@link Value} annotation. 124 * <p>This setter property exists so that developers can provide their own 125 * (non-Spring-specific) annotation type to indicate a default value 126 * expression for a specific argument. 127 */ 128 public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) { 129 this.valueAnnotationType = valueAnnotationType; 130 } 131 132 133 /** 134 * Determine whether the provided bean definition is an autowire candidate. 135 * <p>To be considered a candidate the bean's <em>autowire-candidate</em> 136 * attribute must not have been set to 'false'. Also, if an annotation on 137 * the field or parameter to be autowired is recognized by this bean factory 138 * as a <em>qualifier</em>, the bean must 'match' against the annotation as 139 * well as any attributes it may contain. The bean definition must contain 140 * the same qualifier or match by meta attributes. A "value" attribute will 141 * fallback to match against the bean name or an alias if a qualifier or 142 * attribute does not match. 143 * @see Qualifier 144 */ 145 @Override 146 public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { 147 boolean match = super.isAutowireCandidate(bdHolder, descriptor); 148 if (match) { 149 match = checkQualifiers(bdHolder, descriptor.getAnnotations()); 150 if (match) { 151 MethodParameter methodParam = descriptor.getMethodParameter(); 152 if (methodParam != null) { 153 Method method = methodParam.getMethod(); 154 if (method == null || void.class == method.getReturnType()) { 155 match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations()); 156 } 157 } 158 } 159 } 160 return match; 161 } 162 163 /** 164 * Match the given qualifier annotations against the candidate bean definition. 165 */ 166 protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { 167 if (ObjectUtils.isEmpty(annotationsToSearch)) { 168 return true; 169 } 170 SimpleTypeConverter typeConverter = new SimpleTypeConverter(); 171 for (Annotation annotation : annotationsToSearch) { 172 Class<? extends Annotation> type = annotation.annotationType(); 173 boolean checkMeta = true; 174 boolean fallbackToMeta = false; 175 if (isQualifier(type)) { 176 if (!checkQualifier(bdHolder, annotation, typeConverter)) { 177 fallbackToMeta = true; 178 } 179 else { 180 checkMeta = false; 181 } 182 } 183 if (checkMeta) { 184 boolean foundMeta = false; 185 for (Annotation metaAnn : type.getAnnotations()) { 186 Class<? extends Annotation> metaType = metaAnn.annotationType(); 187 if (isQualifier(metaType)) { 188 foundMeta = true; 189 // Only accept fallback match if @Qualifier annotation has a value... 190 // Otherwise it is just a marker for a custom qualifier annotation. 191 if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || 192 !checkQualifier(bdHolder, metaAnn, typeConverter)) { 193 return false; 194 } 195 } 196 } 197 if (fallbackToMeta && !foundMeta) { 198 return false; 199 } 200 } 201 } 202 return true; 203 } 204 205 /** 206 * Checks whether the given annotation type is a recognized qualifier type. 207 */ 208 protected boolean isQualifier(Class<? extends Annotation> annotationType) { 209 for (Class<? extends Annotation> qualifierType : this.qualifierTypes) { 210 if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) { 211 return true; 212 } 213 } 214 return false; 215 } 216 217 /** 218 * Match the given qualifier annotation against the candidate bean definition. 219 */ 220 protected boolean checkQualifier( 221 BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) { 222 223 Class<? extends Annotation> type = annotation.annotationType(); 224 RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition(); 225 226 AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName()); 227 if (qualifier == null) { 228 qualifier = bd.getQualifier(ClassUtils.getShortName(type)); 229 } 230 if (qualifier == null) { 231 // First, check annotation on qualified element, if any 232 Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type); 233 // Then, check annotation on factory method, if applicable 234 if (targetAnnotation == null) { 235 targetAnnotation = getFactoryMethodAnnotation(bd, type); 236 } 237 if (targetAnnotation == null) { 238 RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd); 239 if (dbd != null) { 240 targetAnnotation = getFactoryMethodAnnotation(dbd, type); 241 } 242 } 243 if (targetAnnotation == null) { 244 // Look for matching annotation on the target class 245 if (getBeanFactory() != null) { 246 try { 247 Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName()); 248 if (beanType != null) { 249 targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type); 250 } 251 } 252 catch (NoSuchBeanDefinitionException ex) { 253 // Not the usual case - simply forget about the type check... 254 } 255 } 256 if (targetAnnotation == null && bd.hasBeanClass()) { 257 targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type); 258 } 259 } 260 if (targetAnnotation != null && targetAnnotation.equals(annotation)) { 261 return true; 262 } 263 } 264 265 Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation); 266 if (attributes.isEmpty() && qualifier == null) { 267 // If no attributes, the qualifier must be present 268 return false; 269 } 270 for (Map.Entry<String, Object> entry : attributes.entrySet()) { 271 String attributeName = entry.getKey(); 272 Object expectedValue = entry.getValue(); 273 Object actualValue = null; 274 // Check qualifier first 275 if (qualifier != null) { 276 actualValue = qualifier.getAttribute(attributeName); 277 } 278 if (actualValue == null) { 279 // Fall back on bean definition attribute 280 actualValue = bd.getAttribute(attributeName); 281 } 282 if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) && 283 expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) { 284 // Fall back on bean name (or alias) match 285 continue; 286 } 287 if (actualValue == null && qualifier != null) { 288 // Fall back on default, but only if the qualifier is present 289 actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName); 290 } 291 if (actualValue != null) { 292 actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass()); 293 } 294 if (!expectedValue.equals(actualValue)) { 295 return false; 296 } 297 } 298 return true; 299 } 300 301 @Nullable 302 protected Annotation getQualifiedElementAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) { 303 AnnotatedElement qualifiedElement = bd.getQualifiedElement(); 304 return (qualifiedElement != null ? AnnotationUtils.getAnnotation(qualifiedElement, type) : null); 305 } 306 307 @Nullable 308 protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) { 309 Method resolvedFactoryMethod = bd.getResolvedFactoryMethod(); 310 return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null); 311 } 312 313 314 /** 315 * Determine whether the given dependency declares an autowired annotation, 316 * checking its required flag. 317 * @see Autowired#required() 318 */ 319 @Override 320 public boolean isRequired(DependencyDescriptor descriptor) { 321 if (!super.isRequired(descriptor)) { 322 return false; 323 } 324 Autowired autowired = descriptor.getAnnotation(Autowired.class); 325 return (autowired == null || autowired.required()); 326 } 327 328 /** 329 * Determine whether the given dependency declares a qualifier annotation. 330 * @see #isQualifier(Class) 331 * @see Qualifier 332 */ 333 @Override 334 public boolean hasQualifier(DependencyDescriptor descriptor) { 335 for (Annotation ann : descriptor.getAnnotations()) { 336 if (isQualifier(ann.annotationType())) { 337 return true; 338 } 339 } 340 return false; 341 } 342 343 /** 344 * Determine whether the given dependency declares a value annotation. 345 * @see Value 346 */ 347 @Override 348 @Nullable 349 public Object getSuggestedValue(DependencyDescriptor descriptor) { 350 Object value = findValue(descriptor.getAnnotations()); 351 if (value == null) { 352 MethodParameter methodParam = descriptor.getMethodParameter(); 353 if (methodParam != null) { 354 value = findValue(methodParam.getMethodAnnotations()); 355 } 356 } 357 return value; 358 } 359 360 /** 361 * Determine a suggested value from any of the given candidate annotations. 362 */ 363 @Nullable 364 protected Object findValue(Annotation[] annotationsToSearch) { 365 if (annotationsToSearch.length > 0) { // qualifier annotations have to be local 366 AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( 367 AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); 368 if (attr != null) { 369 return extractValue(attr); 370 } 371 } 372 return null; 373 } 374 375 /** 376 * Extract the value attribute from the given annotation. 377 * @since 4.3 378 */ 379 protected Object extractValue(AnnotationAttributes attr) { 380 Object value = attr.get(AnnotationUtils.VALUE); 381 if (value == null) { 382 throw new IllegalStateException("Value annotation must have a value attribute"); 383 } 384 return value; 385 } 386 387}