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}