001/* 002 * Copyright 2002-2014 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.context.annotation; 018 019import java.beans.Introspector; 020import java.util.Map; 021import java.util.Set; 022 023import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; 024import org.springframework.beans.factory.config.BeanDefinition; 025import org.springframework.beans.factory.support.BeanDefinitionRegistry; 026import org.springframework.beans.factory.support.BeanNameGenerator; 027import org.springframework.core.annotation.AnnotationAttributes; 028import org.springframework.core.type.AnnotationMetadata; 029import org.springframework.util.ClassUtils; 030import org.springframework.util.StringUtils; 031 032/** 033 * {@link org.springframework.beans.factory.support.BeanNameGenerator} 034 * implementation for bean classes annotated with the 035 * {@link org.springframework.stereotype.Component @Component} annotation 036 * or with another annotation that is itself annotated with 037 * {@link org.springframework.stereotype.Component @Component} as a 038 * meta-annotation. For example, Spring's stereotype annotations (such as 039 * {@link org.springframework.stereotype.Repository @Repository}) are 040 * themselves annotated with 041 * {@link org.springframework.stereotype.Component @Component}. 042 * 043 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and 044 * JSR-330's {@link javax.inject.Named} annotations, if available. Note that 045 * Spring component annotations always override such standard annotations. 046 * 047 * <p>If the annotation's value doesn't indicate a bean name, an appropriate 048 * name will be built based on the short name of the class (with the first 049 * letter lower-cased). For example: 050 * 051 * <pre class="code">com.xyz.FooServiceImpl -> fooServiceImpl</pre> 052 * 053 * @author Juergen Hoeller 054 * @author Mark Fisher 055 * @since 2.5 056 * @see org.springframework.stereotype.Component#value() 057 * @see org.springframework.stereotype.Repository#value() 058 * @see org.springframework.stereotype.Service#value() 059 * @see org.springframework.stereotype.Controller#value() 060 * @see javax.inject.Named#value() 061 */ 062public class AnnotationBeanNameGenerator implements BeanNameGenerator { 063 064 private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component"; 065 066 067 @Override 068 public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { 069 if (definition instanceof AnnotatedBeanDefinition) { 070 String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); 071 if (StringUtils.hasText(beanName)) { 072 // Explicit bean name found. 073 return beanName; 074 } 075 } 076 // Fallback: generate a unique default bean name. 077 return buildDefaultBeanName(definition, registry); 078 } 079 080 /** 081 * Derive a bean name from one of the annotations on the class. 082 * @param annotatedDef the annotation-aware bean definition 083 * @return the bean name, or {@code null} if none is found 084 */ 085 protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) { 086 AnnotationMetadata amd = annotatedDef.getMetadata(); 087 Set<String> types = amd.getAnnotationTypes(); 088 String beanName = null; 089 for (String type : types) { 090 AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type); 091 if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) { 092 Object value = attributes.get("value"); 093 if (value instanceof String) { 094 String strVal = (String) value; 095 if (StringUtils.hasLength(strVal)) { 096 if (beanName != null && !strVal.equals(beanName)) { 097 throw new IllegalStateException("Stereotype annotations suggest inconsistent " + 098 "component names: '" + beanName + "' versus '" + strVal + "'"); 099 } 100 beanName = strVal; 101 } 102 } 103 } 104 } 105 return beanName; 106 } 107 108 /** 109 * Check whether the given annotation is a stereotype that is allowed 110 * to suggest a component name through its annotation {@code value()}. 111 * @param annotationType the name of the annotation class to check 112 * @param metaAnnotationTypes the names of meta-annotations on the given annotation 113 * @param attributes the map of attributes for the given annotation 114 * @return whether the annotation qualifies as a stereotype with component name 115 */ 116 protected boolean isStereotypeWithNameValue(String annotationType, 117 Set<String> metaAnnotationTypes, Map<String, Object> attributes) { 118 119 boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) || 120 (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) || 121 annotationType.equals("javax.annotation.ManagedBean") || 122 annotationType.equals("javax.inject.Named"); 123 124 return (isStereotype && attributes != null && attributes.containsKey("value")); 125 } 126 127 /** 128 * Derive a default bean name from the given bean definition. 129 * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}. 130 * @param definition the bean definition to build a bean name for 131 * @param registry the registry that the given bean definition is being registered with 132 * @return the default bean name (never {@code null}) 133 */ 134 protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { 135 return buildDefaultBeanName(definition); 136 } 137 138 /** 139 * Derive a default bean name from the given bean definition. 140 * <p>The default implementation simply builds a decapitalized version 141 * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao". 142 * <p>Note that inner classes will thus have names of the form 143 * "outerClassName.InnerClassName", which because of the period in the 144 * name may be an issue if you are autowiring by name. 145 * @param definition the bean definition to build a bean name for 146 * @return the default bean name (never {@code null}) 147 */ 148 protected String buildDefaultBeanName(BeanDefinition definition) { 149 String shortClassName = ClassUtils.getShortName(definition.getBeanClassName()); 150 return Introspector.decapitalize(shortClassName); 151 } 152 153}