1 /* 2 * Copyright 2002-2014 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.context.annotation; 18 19 import java.beans.Introspector; 20 import java.util.Map; 21 import java.util.Set; 22 23 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; 24 import org.springframework.beans.factory.config.BeanDefinition; 25 import org.springframework.beans.factory.support.BeanDefinitionRegistry; 26 import org.springframework.beans.factory.support.BeanNameGenerator; 27 import org.springframework.core.annotation.AnnotationAttributes; 28 import org.springframework.core.type.AnnotationMetadata; 29 import org.springframework.util.ClassUtils; 30 import org.springframework.util.StringUtils; 31 32 /** 33 * {@link org.springframework.beans.factory.support.BeanNameGenerator} 34 * implementation for bean classes annotated with the 35 * {@link org.springframework.stereotype.Component @Component} annotation 36 * or with another annotation that is itself annotated with 37 * {@link org.springframework.stereotype.Component @Component} as a 38 * meta-annotation. For example, Spring's stereotype annotations (such as 39 * {@link org.springframework.stereotype.Repository @Repository}) are 40 * themselves annotated with 41 * {@link org.springframework.stereotype.Component @Component}. 42 * 43 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and 44 * JSR-330's {@link javax.inject.Named} annotations, if available. Note that 45 * Spring component annotations always override such standard annotations. 46 * 47 * <p>If the annotation's value doesn't indicate a bean name, an appropriate 48 * name will be built based on the short name of the class (with the first 49 * letter lower-cased). For example: 50 * 51 * <pre class="code">com.xyz.FooServiceImpl -> fooServiceImpl</pre> 52 * 53 * @author Juergen Hoeller 54 * @author Mark Fisher 55 * @since 2.5 56 * @see org.springframework.stereotype.Component#value() 57 * @see org.springframework.stereotype.Repository#value() 58 * @see org.springframework.stereotype.Service#value() 59 * @see org.springframework.stereotype.Controller#value() 60 * @see javax.inject.Named#value() 61 */ 62 public class AnnotationBeanNameGenerator implements BeanNameGenerator { 63 64 private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component"; 65 66 67 @Override 68 public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { 69 if (definition instanceof AnnotatedBeanDefinition) { 70 String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); 71 if (StringUtils.hasText(beanName)) { 72 // Explicit bean name found. 73 return beanName; 74 } 75 } 76 // Fallback: generate a unique default bean name. 77 return buildDefaultBeanName(definition, registry); 78 } 79 80 /** 81 * Derive a bean name from one of the annotations on the class. 82 * @param annotatedDef the annotation-aware bean definition 83 * @return the bean name, or {@code null} if none is found 84 */ 85 protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) { 86 AnnotationMetadata amd = annotatedDef.getMetadata(); 87 Set<String> types = amd.getAnnotationTypes(); 88 String beanName = null; 89 for (String type : types) { 90 AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type); 91 if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) { 92 Object value = attributes.get("value"); 93 if (value instanceof String) { 94 String strVal = (String) value; 95 if (StringUtils.hasLength(strVal)) { 96 if (beanName != null && !strVal.equals(beanName)) { 97 throw new IllegalStateException("Stereotype annotations suggest inconsistent " + 98 "component names: '" + beanName + "' versus '" + strVal + "'"); 99 } 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 }