Open Source Repository

Home /spring/spring-beans-3.0.5 | Repository Home



org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
 {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers
 * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired.
 * Also supports suggested expression values through a {@link Value value} annotation.
 *
 <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
 *
 @author Mark Fisher
 @author Juergen Hoeller
 @since 2.5
 @see AutowireCandidateQualifier
 @see Qualifier
 @see Value
 */
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {

  private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<Class<? extends Annotation>>();

  private Class<? extends Annotation> valueAnnotationType = Value.class;

  private BeanFactory beanFactory;


  /**
   * Create a new QualifierAnnotationAutowireCandidateResolver
   * for Spring's standard {@link Qualifier} annotation.
   <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
   */
  @SuppressWarnings("unchecked")
  public QualifierAnnotationAutowireCandidateResolver() {
    this.qualifierTypes.add(Qualifier.class);
    ClassLoader cl = QualifierAnnotationAutowireCandidateResolver.class.getClassLoader();
    try {
      this.qualifierTypes.add((Class<? extends Annotation>cl.loadClass("javax.inject.Qualifier"));
    }
    catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
    }
  }

  /**
   * Create a new QualifierAnnotationAutowireCandidateResolver
   * for the given qualifier annotation type.
   @param qualifierType the qualifier annotation to look for
   */
  public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) {
    Assert.notNull(qualifierType, "'qualifierType' must not be null");
    this.qualifierTypes.add(qualifierType);
  }

  /**
   * Create a new QualifierAnnotationAutowireCandidateResolver
   * for the given qualifier annotation types.
   @param qualifierTypes the qualifier annotations to look for
   */
  public QualifierAnnotationAutowireCandidateResolver(Set<Class<? extends Annotation>> qualifierTypes) {
    Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null");
    this.qualifierTypes.addAll(qualifierTypes);
  }


  /**
   * Register the given type to be used as a qualifier when autowiring.
   <p>This identifies qualifier annotations for direct use (on fields,
   * method parameters and constructor parameters) as well as meta
   * annotations that in turn identify actual qualifier annotations.
   <p>This implementation only supports annotations as qualifier types.
   * The default is Spring's {@link Qualifier} annotation which serves
   * as a qualifier for direct use and also as a meta annotation.
   @param qualifierType the annotation type to register
   */
  public void addQualifierType(Class<? extends Annotation> qualifierType) {
    this.qualifierTypes.add(qualifierType);
  }

  /**
   * Set the 'value' annotation type, to be used on fields, method parameters
   * and constructor parameters.
   <p>The default value annotation type is the Spring-provided
   {@link Value} annotation.
   <p>This setter property exists so that developers can provide their own
   * (non-Spring-specific) annotation type to indicate a default value
   * expression for a specific argument.
   */
  public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
    this.valueAnnotationType = valueAnnotationType;
  }

  public void setBeanFactory(BeanFactory beanFactory) {
    this.beanFactory = beanFactory;
  }


  /**
   * Determine whether the provided bean definition is an autowire candidate.
   <p>To be considered a candidate the bean's <em>autowire-candidate</em>
   * attribute must not have been set to 'false'. Also, if an annotation on
   * the field or parameter to be autowired is recognized by this bean factory
   * as a <em>qualifier</em>, the bean must 'match' against the annotation as
   * well as any attributes it may contain. The bean definition must contain
   * the same qualifier or match by meta attributes. A "value" attribute will
   * fallback to match against the bean name or an alias if a qualifier or
   * attribute does not match.
   @see Qualifier
   */
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
      // if explicitly false, do not proceed with qualifier check
      return false;
    }
    if (descriptor == null) {
      // no qualification necessary
      return true;
    }
    boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
    if (match) {
      MethodParameter methodParam = descriptor.getMethodParameter();
      if (methodParam != null) {
        Method method = methodParam.getMethod();
        if (method == null || void.class.equals(method.getReturnType())) {
          match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
        }
      }
    }
    return match;
  }

  /**
   * Match the given qualifier annotations against the candidate bean definition.
   */
  protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
    if (ObjectUtils.isEmpty(annotationsToSearch)) {
      return true;
    }
    SimpleTypeConverter typeConverter = new SimpleTypeConverter();
    for (Annotation annotation : annotationsToSearch) {
      Class<? extends Annotation> type = annotation.annotationType();
      if (isQualifier(type)) {
        if (!checkQualifier(bdHolder, annotation, typeConverter)) {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Checks whether the given annotation type is a recognized qualifier type.
   */
  protected boolean isQualifier(Class<? extends Annotation> annotationType) {
    for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
      if (annotationType.equals(qualifierType|| annotationType.isAnnotationPresent(qualifierType)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Match the given qualifier annotation against the candidate bean definition.
   */
  protected boolean checkQualifier(
      BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {

    Class<? extends Annotation> type = annotation.annotationType();
    RootBeanDefinition bd = (RootBeanDefinitionbdHolder.getBeanDefinition();
    AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
    if (qualifier == null) {
      qualifier = bd.getQualifier(ClassUtils.getShortName(type));
    }
    if (qualifier == null) {
      Annotation targetAnnotation = null;
      if (bd.getResolvedFactoryMethod() != null) {
        targetAnnotation = bd.getResolvedFactoryMethod().getAnnotation(type);
      }
      if (targetAnnotation == null) {
        // look for matching annotation on the target class
        if (this.beanFactory != null) {
          Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
          if (beanType != null) {
            targetAnnotation = ClassUtils.getUserClass(beanType).getAnnotation(type);
          }
        }
        if (targetAnnotation == null && bd.hasBeanClass()) {
          targetAnnotation = ClassUtils.getUserClass(bd.getBeanClass()).getAnnotation(type);
        }
      }
      if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
        return true;
      }
    }
    Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
    if (attributes.isEmpty() && qualifier == null) {
      // if no attributes, the qualifier must be present
      return false;
    }
    for (Map.Entry<String, Object> entry : attributes.entrySet()) {
      String attributeName = entry.getKey();
      Object expectedValue = entry.getValue();
      Object actualValue = null;
      // check qualifier first
      if (qualifier != null) {
        actualValue = qualifier.getAttribute(attributeName);
      }
      if (actualValue == null) {
        // fall back on bean definition attribute
        actualValue = bd.getAttribute(attributeName);
      }
      if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY&&
          expectedValue instanceof String && bdHolder.matchesName((StringexpectedValue)) {
        // fall back on bean name (or alias) match
        continue;
      }
      if (actualValue == null && qualifier != null) {
        // fall back on default, but only if the qualifier is present
        actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
      }
      if (actualValue != null) {
        actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
      }
      if (!expectedValue.equals(actualValue)) {
        return false;
      }
    }
    return true;
  }


  /**
   * Determine whether the given dependency carries a value annotation.
   @see Value
   */
  public Object getSuggestedValue(DependencyDescriptor descriptor) {
    Object value = findValue(descriptor.getAnnotations());
    if (value == null) {
      MethodParameter methodParam = descriptor.getMethodParameter();
      if (methodParam != null) {
        value = findValue(methodParam.getMethodAnnotations());
      }
    }
    return value;
  }

  /**
   * Determine a suggested value from any of the given candidate annotations.
   */
  protected Object findValue(Annotation[] annotationsToSearch) {
    for (Annotation annotation : annotationsToSearch) {
      if (this.valueAnnotationType.isInstance(annotation)) {
        Object value = AnnotationUtils.getValue(annotation);
        if (value == null) {
          throw new IllegalStateException("Value annotation must have a value attribute");
        }
        return value;
      }
    }
    return null;
  }

}