Open Source Repository

Home /spring/spring-context-3.0.5 | Repository Home


org/springframework/context/annotation/AnnotationBeanNameGenerator.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.context.annotation;

import java.beans.Introspector;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 {@link org.springframework.beans.factory.support.BeanNameGenerator}
 * implementation for bean classes annotated with the
 {@link org.springframework.stereotype.Component @Component} annotation
 * or with another annotation that is itself annotated with
 {@link org.springframework.stereotype.Component @Component} as a
 * meta-annotation. For example, Spring's stereotype annotations (such as
 {@link org.springframework.stereotype.Repository @Repository}) are
 * themselves annotated with 
 {@link org.springframework.stereotype.Component @Component}.
 *
 <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
 * JSR-330's {@link javax.inject.Named} annotations, if available. Note that
 * Spring component annotations always override such standard annotations.
 *
 <p>If the annotation's value doesn't indicate a bean name, an appropriate
 * name will be built based on the short name of the class (with the first
 * letter lower-cased). For example:
 *
 <pre class="code">com.xyz.FooServiceImpl -&gt; fooServiceImpl</pre>
 *
 @author Juergen Hoeller
 @author Mark Fisher
 @since 2.5
 @see org.springframework.stereotype.Component#value()
 @see org.springframework.stereotype.Repository#value()
 @see org.springframework.stereotype.Service#value()
 @see org.springframework.stereotype.Controller#value()
 @see javax.inject.Named#value()
 */
public class AnnotationBeanNameGenerator implements BeanNameGenerator {

  private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";


  public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    if (definition instanceof AnnotatedBeanDefinition) {
      String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinitiondefinition);
      if (StringUtils.hasText(beanName)) {
        // Explicit bean name found.
        return beanName;
      }
    }
    // Fallback: generate a unique default bean name.
    return buildDefaultBeanName(definition);
  }

  /**
   * Derive a bean name from one of the annotations on the class.
   @param annotatedDef the annotation-aware bean definition
   @return the bean name, or <code>null</code> if none is found
   */
  protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
    AnnotationMetadata amd = annotatedDef.getMetadata();
    Set<String> types = amd.getAnnotationTypes();
    String beanName = null;
    for (String type : types) {
      Map<String, Object> attributes = amd.getAnnotationAttributes(type);
      if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
        String value = (Stringattributes.get("value");
        if (StringUtils.hasLength(value)) {
          if (beanName != null && !value.equals(beanName)) {
            throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                "component names: '" + beanName + "' versus '" + value + "'");
          }
          beanName = value;
        }
      }
    }
    return beanName;
  }

  /**
   * Check whether the given annotation is a stereotype that is allowed
   * to suggest a component name through its annotation <code>value()</code>.
   @param annotationType the name of the annotation class to check
   @param metaAnnotationTypes the names of meta-annotations on the given annotation
   @param attributes the map of attributes for the given annotation
   @return whether the annotation qualifies as a stereotype with component name
   */
  protected boolean isStereotypeWithNameValue(String annotationType,
      Set<String> metaAnnotationTypes, Map<String, Object> attributes) {

    boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME||
        (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) ||
        annotationType.equals("javax.annotation.ManagedBean"||
        annotationType.equals("javax.inject.Named");
    return (isStereotype && attributes != null && attributes.containsKey("value"));
  }

  /**
   * Derive a default bean name from the given bean definition.
   <p>The default implementation simply builds a decapitalized version
   * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
   <p>Note that inner classes will thus have names of the form
   * "outerClassName.innerClassName", which because of the period in the
   * name may be an issue if you are autowiring by name.
   @param definition the bean definition to build a bean name for
   @return the default bean name (never <code>null</code>)
   */
  protected String buildDefaultBeanName(BeanDefinition definition) {
    String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
    return Introspector.decapitalize(shortClassName);
  }

}