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