---恢復內容開始---
接上篇,上篇解析了DefaultBeanGenerator生成bean name的過程(http://www.cnblogs.com/jason0529/p/5272265.html ), 本篇我們繼續解析另一類bean name生成方式。
spring定義bean有兩種模式,配置文件(xml,properties)和注解。注:jpa的聲明接口生成bean應該可以算第三種模式,這里不討論。
對兩種bean定義方式,spring提供了兩種不同的bean name實現方式去實現不同的模式。AnnotationBeanNameGenerator能夠處理 Component,Respository,Service,Controller這四個常用的注解,解析為bean name注給他們對應的value屬性。另外jee的javax.annotation.ManagedBean和javax.inject.Named也可以支持。
當Component,Respository,Service,Controller注解的value樹形沒有自定義時,會根據類的名稱生成一個短的bean name。例如: com.xyz.FooServiceImpl -> fooServiceImpl
入口肯定是BeanNameGenerator接口聲明的generateBeanName(BeanDefinition, BeanDefinitionRegistry) 方法,該方法做了一個分類判斷,處理AnnotationBeanDefinition和default兩種方式的。
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { //判斷是否是否是AnnotatedBeanDefinition的子類, AnnotatedBeanDefinition是BeanDefinition的一個子類 //如果是AnnotatedBeanDefinition , 按照注解生成模式生成信息,否則生成默認的bean nameif (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); //保證生成的bean name 非空if (StringUtils.hasText(beanName)) { // Explicit bean name found.return beanName; } } // Fallback: generate a unique default bean name.return buildDefaultBeanName(definition, registry); }
先從相對簡單的default看起,這段代碼的疑點是生成的bean name並沒有和DefaultBeanNameGenerator一樣做唯一性校驗,可能導致不同包下面存在相同的類名時,會產生兩個name一樣的bean,引發spring 異常。
/** * Derive a default bean name from the given bean definition. * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}. * @param definition the bean definition to build a bean name for * @param registry the registry that the given bean definition is being registered with * @return the default bean name (never {@code null}) */protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
//沒有做 name 唯一性校驗。 return buildDefaultBeanName(definition); } /** * 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}) */protected String buildDefaultBeanName(BeanDefinition definition) {
//具體類名獲取和格式化先不做具體討論 String shortClassName = ClassUtils.getShortName(definition.getBeanClassName()); return Introspector.decapitalize(shortClassName); }
---恢復內容結束---
接上篇,上篇解析了DefaultBeanGenerator生成bean name的過程(http://www.cnblogs.com/jason0529/p/5272265.html ), 本篇我們繼續解析另一類bean name生成方式。
spring定義bean有兩種模式,配置文件(xml,properties)和注解。注:jpa的聲明接口生成bean應該可以算第三種模式,這里不討論。
對兩種bean定義方式,spring提供了兩種不同的bean name實現方式去實現不同的模式。AnnotationBeanNameGenerator能夠處理 Component,Respository,Service,Controller這四個常用的注解,解析為bean name注給他們對應的value屬性。另外jee的javax.annotation.ManagedBean和javax.inject.Named也可以支持。
當Component,Respository,Service,Controller注解的value樹形沒有自定義時,會根據類的名稱生成一個短的bean name。例如: com.xyz.FooServiceImpl -> fooServiceImpl
入口肯定是BeanNameGenerator接口聲明的generateBeanName(BeanDefinition, BeanDefinitionRegistry) 方法,該方法做了一個分類判斷,處理AnnotationBeanDefinition和default兩種方式的。
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { //判斷是否是否是AnnotatedBeanDefinition的子類, AnnotatedBeanDefinition是BeanDefinition的一個子類 //如果是AnnotatedBeanDefinition , 按照注解生成模式生成信息,否則生成默認的bean nameif (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); //保證生成的bean name 非空if (StringUtils.hasText(beanName)) { // Explicit bean name found.return beanName; } } // Fallback: generate a unique default bean name.return buildDefaultBeanName(definition, registry); }
先從相對簡單的default看起,這段代碼的疑點是生成的bean name並沒有和DefaultBeanNameGenerator一樣做唯一性校驗,可能導致不同包下面存在相同的類名時,會產生兩個name一樣的bean,引發spring 異常。
/** * Derive a default bean name from the given bean definition. * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}. * @param definition the bean definition to build a bean name for * @param registry the registry that the given bean definition is being registered with * @return the default bean name (never {@code null}) */protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
//沒有做 name 唯一性校驗。 return buildDefaultBeanName(definition); } /** * 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}) */protected String buildDefaultBeanName(BeanDefinition definition) {
//具體類名獲取和格式化先不做具體討論 String shortClassName = ClassUtils.getShortName(definition.getBeanClassName()); return Introspector.decapitalize(shortClassName); }
其實ClassUtils.getShortName也很簡單,根據傳入字符串獲取一個具體類名稱,不含包路徑,考慮cglib代理的類,做了一個特殊處理。
/** * Get the class name without the qualified package name. * @param className the className to get the short name for * @return the class name of the class without the package name * @throws IllegalArgumentException if the className is empty */public static String getShortName(String className) { Assert.hasLength(className, "Class name must not be empty"); int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR); int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR); if (nameEndIndex == -1) { nameEndIndex = className.length(); } String shortName = className.substring(lastDotIndex + 1, nameEndIndex); shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR); return shortName; }
接下來看第二條線,AnnotationBeanDefinition的beanName如何生成,具體處理委托給了determineBeanNameFromAnnotation(AnnotatedBeanDefinition)方法完成,該方法對該類的所有注解進行了遍歷,滿足三個條件:注解名稱在待解析列表,存在value屬性且非空,當且只有一個注解正確配置value屬性。
/** * Derive a bean name from one of the annotations on the class. * 從類的注解中包含value屬性的注解生成一個bean name * @param annotatedDef the annotation-aware bean definition * @return the bean name, or {@code null} 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) { //獲取該類型對應的屬性 AnnotationAttributes attributes = MetadataUtils.attributesFor(amd, type); //判斷注解類型是否包含value屬性if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) { String value = (String) attributes.get("value"); if (StringUtils.hasLength(value)) { //不多於1個注解配置了value屬性且非空,比如無法在一個類上面同時使用Component和Sevice注解同時配置beanName值if (beanName != null && !value.equals(beanName)) { throw new IllegalStateException("Stereotype annotations suggest inconsistent " + "component names: '" + beanName + "' versus '" + value + "'"); } beanName = value; } } } return beanName; }
這個方法里面有兩個關鍵的處理流程,第一步,讀取對應annotationType對應的所有屬性。
public static AnnotationAttributes attributesFor(AnnotationMetadata metadata, String annoClassName) { return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annoClassName, false)); }
//AnnotationMetadata.java /** * Retrieve the attributes of the annotation of the given type, * if any (i.e. if defined on the underlying class, as direct * annotation or as meta-annotation). * @param annotationType the annotation type to look for * @param classValuesAsString whether to convert class references to String * class names for exposure as values in the returned Map, instead of Class * references which might potentially have to be loaded first * @return a Map of attributes, with the attribute name as key (e.g. "value") * and the defined attribute value as Map value. This return value will be * {@code null} if no matching annotation is defined. */ Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
第二步,判斷annotationType是否具有value屬性,但是metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)這部分並不是很懂?注解無法繼承,為啥要多判斷一次?
private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component"; /** * Check whether the given annotation is a stereotype that is allowed * to suggest a component name through its annotation {@code value()}. * @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")); }
整理下思路:
生成bean name有兩條處理線,使用AnnotationBeanDefinition注解和不使用的。
不使用AnnotationBeanDefinition注解的,直接將類名(不含包名)改為駝峰形式作為bean name。
使用AnnotationBeanDefinition注解的:
1,讀取所有注解類型
2,便利所有注解類型,找到所有為Component、Service,Respository,Controller含有非空value屬性的注解
3,不多於一個個有效配置時生效,大於一個會拋出異常。(spring無法明確具體哪個生效)