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