View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.beans.factory.annotation;
18  
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.Method;
21  import java.util.LinkedHashSet;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.springframework.beans.SimpleTypeConverter;
26  import org.springframework.beans.TypeConverter;
27  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
28  import org.springframework.beans.factory.config.BeanDefinitionHolder;
29  import org.springframework.beans.factory.config.DependencyDescriptor;
30  import org.springframework.beans.factory.support.AutowireCandidateQualifier;
31  import org.springframework.beans.factory.support.AutowireCandidateResolver;
32  import org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver;
33  import org.springframework.beans.factory.support.RootBeanDefinition;
34  import org.springframework.core.MethodParameter;
35  import org.springframework.core.annotation.AnnotationUtils;
36  import org.springframework.util.Assert;
37  import org.springframework.util.ClassUtils;
38  import org.springframework.util.ObjectUtils;
39  import org.springframework.util.StringUtils;
40  
41  /**
42   * {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers
43   * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired.
44   * Also supports suggested expression values through a {@link Value value} annotation.
45   *
46   * <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
47   *
48   * @author Mark Fisher
49   * @author Juergen Hoeller
50   * @since 2.5
51   * @see AutowireCandidateQualifier
52   * @see Qualifier
53   * @see Value
54   */
55  public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
56  
57  	private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<Class<? extends Annotation>>(2);
58  
59  	private Class<? extends Annotation> valueAnnotationType = Value.class;
60  
61  
62  	/**
63  	 * Create a new QualifierAnnotationAutowireCandidateResolver
64  	 * for Spring's standard {@link Qualifier} annotation.
65  	 * <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
66  	 */
67  	@SuppressWarnings("unchecked")
68  	public QualifierAnnotationAutowireCandidateResolver() {
69  		this.qualifierTypes.add(Qualifier.class);
70  		try {
71  			this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier",
72  							QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
73  		}
74  		catch (ClassNotFoundException ex) {
75  			// JSR-330 API not available - simply skip.
76  		}
77  	}
78  
79  	/**
80  	 * Create a new QualifierAnnotationAutowireCandidateResolver
81  	 * for the given qualifier annotation type.
82  	 * @param qualifierType the qualifier annotation to look for
83  	 */
84  	public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) {
85  		Assert.notNull(qualifierType, "'qualifierType' must not be null");
86  		this.qualifierTypes.add(qualifierType);
87  	}
88  
89  	/**
90  	 * Create a new QualifierAnnotationAutowireCandidateResolver
91  	 * for the given qualifier annotation types.
92  	 * @param qualifierTypes the qualifier annotations to look for
93  	 */
94  	public QualifierAnnotationAutowireCandidateResolver(Set<Class<? extends Annotation>> qualifierTypes) {
95  		Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null");
96  		this.qualifierTypes.addAll(qualifierTypes);
97  	}
98  
99  
100 	/**
101 	 * Register the given type to be used as a qualifier when autowiring.
102 	 * <p>This identifies qualifier annotations for direct use (on fields,
103 	 * method parameters and constructor parameters) as well as meta
104 	 * annotations that in turn identify actual qualifier annotations.
105 	 * <p>This implementation only supports annotations as qualifier types.
106 	 * The default is Spring's {@link Qualifier} annotation which serves
107 	 * as a qualifier for direct use and also as a meta annotation.
108 	 * @param qualifierType the annotation type to register
109 	 */
110 	public void addQualifierType(Class<? extends Annotation> qualifierType) {
111 		this.qualifierTypes.add(qualifierType);
112 	}
113 
114 	/**
115 	 * Set the 'value' annotation type, to be used on fields, method parameters
116 	 * and constructor parameters.
117 	 * <p>The default value annotation type is the Spring-provided
118 	 * {@link Value} annotation.
119 	 * <p>This setter property exists so that developers can provide their own
120 	 * (non-Spring-specific) annotation type to indicate a default value
121 	 * expression for a specific argument.
122 	 */
123 	public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
124 		this.valueAnnotationType = valueAnnotationType;
125 	}
126 
127 
128 	/**
129 	 * Determine whether the provided bean definition is an autowire candidate.
130 	 * <p>To be considered a candidate the bean's <em>autowire-candidate</em>
131 	 * attribute must not have been set to 'false'. Also, if an annotation on
132 	 * the field or parameter to be autowired is recognized by this bean factory
133 	 * as a <em>qualifier</em>, the bean must 'match' against the annotation as
134 	 * well as any attributes it may contain. The bean definition must contain
135 	 * the same qualifier or match by meta attributes. A "value" attribute will
136 	 * fallback to match against the bean name or an alias if a qualifier or
137 	 * attribute does not match.
138 	 * @see Qualifier
139 	 */
140 	@Override
141 	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
142 		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
143 		if (match && descriptor != null) {
144 			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
145 			if (match) {
146 				MethodParameter methodParam = descriptor.getMethodParameter();
147 				if (methodParam != null) {
148 					Method method = methodParam.getMethod();
149 					if (method == null || void.class.equals(method.getReturnType())) {
150 						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
151 					}
152 				}
153 			}
154 		}
155 		return match;
156 	}
157 
158 	/**
159 	 * Match the given qualifier annotations against the candidate bean definition.
160 	 */
161 	protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
162 		if (ObjectUtils.isEmpty(annotationsToSearch)) {
163 			return true;
164 		}
165 		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
166 		for (Annotation annotation : annotationsToSearch) {
167 			Class<? extends Annotation> type = annotation.annotationType();
168 			boolean checkMeta = true;
169 			boolean fallbackToMeta = false;
170 			if (isQualifier(type)) {
171 				if (!checkQualifier(bdHolder, annotation, typeConverter)) {
172 					fallbackToMeta = true;
173 				}
174 				else {
175 					checkMeta = false;
176 				}
177 			}
178 			if (checkMeta) {
179 				boolean foundMeta = false;
180 				for (Annotation metaAnn : type.getAnnotations()) {
181 					Class<? extends Annotation> metaType = metaAnn.annotationType();
182 					if (isQualifier(metaType)) {
183 						foundMeta = true;
184 						// Only accept fallback match if @Qualifier annotation has a value...
185 						// Otherwise it is just a marker for a custom qualifier annotation.
186 						if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
187 								!checkQualifier(bdHolder, metaAnn, typeConverter)) {
188 							return false;
189 						}
190 					}
191 				}
192 				if (fallbackToMeta && !foundMeta) {
193 					return false;
194 				}
195 			}
196 		}
197 		return true;
198 	}
199 
200 	/**
201 	 * Checks whether the given annotation type is a recognized qualifier type.
202 	 */
203 	protected boolean isQualifier(Class<? extends Annotation> annotationType) {
204 		for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
205 			if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
206 				return true;
207 			}
208 		}
209 		return false;
210 	}
211 
212 	/**
213 	 * Match the given qualifier annotation against the candidate bean definition.
214 	 */
215 	protected boolean checkQualifier(
216 			BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
217 
218 		Class<? extends Annotation> type = annotation.annotationType();
219 		RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
220 
221 		AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
222 		if (qualifier == null) {
223 			qualifier = bd.getQualifier(ClassUtils.getShortName(type));
224 		}
225 		if (qualifier == null) {
226 			// First, check annotation on factory method, if applicable
227 			Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type);
228 			if (targetAnnotation == null) {
229 				RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
230 				if (dbd != null) {
231 					targetAnnotation = getFactoryMethodAnnotation(dbd, type);
232 				}
233 			}
234 			if (targetAnnotation == null) {
235 				// Look for matching annotation on the target class
236 				if (getBeanFactory() != null) {
237 					try {
238 						Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
239 						if (beanType != null) {
240 							targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
241 						}
242 					}
243 					catch (NoSuchBeanDefinitionException ex) {
244 						// Not the usual case - simply forget about the type check...
245 					}
246 				}
247 				if (targetAnnotation == null && bd.hasBeanClass()) {
248 					targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);
249 				}
250 			}
251 			if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
252 				return true;
253 			}
254 		}
255 
256 		Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
257 		if (attributes.isEmpty() && qualifier == null) {
258 			// If no attributes, the qualifier must be present
259 			return false;
260 		}
261 		for (Map.Entry<String, Object> entry : attributes.entrySet()) {
262 			String attributeName = entry.getKey();
263 			Object expectedValue = entry.getValue();
264 			Object actualValue = null;
265 			// Check qualifier first
266 			if (qualifier != null) {
267 				actualValue = qualifier.getAttribute(attributeName);
268 			}
269 			if (actualValue == null) {
270 				// Fall back on bean definition attribute
271 				actualValue = bd.getAttribute(attributeName);
272 			}
273 			if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
274 					expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
275 				// Fall back on bean name (or alias) match
276 				continue;
277 			}
278 			if (actualValue == null && qualifier != null) {
279 				// Fall back on default, but only if the qualifier is present
280 				actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
281 			}
282 			if (actualValue != null) {
283 				actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
284 			}
285 			if (!expectedValue.equals(actualValue)) {
286 				return false;
287 			}
288 		}
289 		return true;
290 	}
291 
292 	protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) {
293 		Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
294 		return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null);
295 	}
296 
297 
298 	/**
299 	 * Determine whether the given dependency carries a value annotation.
300 	 * @see Value
301 	 */
302 	@Override
303 	public Object getSuggestedValue(DependencyDescriptor descriptor) {
304 		Object value = findValue(descriptor.getAnnotations());
305 		if (value == null) {
306 			MethodParameter methodParam = descriptor.getMethodParameter();
307 			if (methodParam != null) {
308 				value = findValue(methodParam.getMethodAnnotations());
309 			}
310 		}
311 		return value;
312 	}
313 
314 	/**
315 	 * Determine a suggested value from any of the given candidate annotations.
316 	 */
317 	protected Object findValue(Annotation[] annotationsToSearch) {
318 		for (Annotation annotation : annotationsToSearch) {
319 			if (this.valueAnnotationType.isInstance(annotation)) {
320 				return extractValue(annotation);
321 			}
322 		}
323 		for (Annotation annotation : annotationsToSearch) {
324 			Annotation metaAnn = annotation.annotationType().getAnnotation(this.valueAnnotationType);
325 			if (metaAnn != null) {
326 				return extractValue(metaAnn);
327 			}
328 		}
329 		return null;
330 	}
331 
332 	/**
333 	 * Extract the value attribute from the given annotation.
334 	 */
335 	protected Object extractValue(Annotation valueAnnotation) {
336 		Object value = AnnotationUtils.getValue(valueAnnotation);
337 		if (value == null) {
338 			throw new IllegalStateException("Value annotation must have a value attribute");
339 		}
340 		return value;
341 	}
342 
343 }