---恢复内容开始---

  接上篇,上篇解析了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 name
if (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 name
if (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无法明确具体哪个生效)

Spring源码入门——AnnotationBeanNameGenerator解析的更多相关文章

  1. Spring源码入门——DefaultBeanNameGenerator解析 转发 https://www.cnblogs.com/jason0529/p/5272265.html

    Spring源码入门——DefaultBeanNameGenerator解析   我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指 ...

  2. Spring源码入门——XmlBeanDefinitionReader解析

    接上篇[] ,我们看到BeanDefinitionReader解决的是从资源文件(xml,propert)到BeanDefinition集合的过程.所以BeanDefinitionReader接口有两 ...

  3. Spring源码入门——DefaultBeanNameGenerator解析

    我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指定其id和name值,但那些没有指定的,或者注解的spring的beanname怎 ...

  4. 从零开始学spring源码之xml解析(一):入门

    谈到spring,首先想到的肯定是ioc,DI依赖注入,aop,但是其实很多人只是知道这些是spring核心概念,甚至不知道这些代表了什么意思,,作为一个java程序员,怎么能说自己对号称改变了jav ...

  5. 从零开始学spring源码之xml解析(二):默认标签和自定义标签解析

    默认标签: 上一篇说到spring的默认标签和自定义标签,发现这里面东西还蛮多的.决定还是拆开来写.今天就来好好聊聊这两块是怎么玩的,首先我们先看看默认标签: private void parseDe ...

  6. Spring源码-入门

    一.测试类 public class Main { public static void main(String[] args) { ApplicationContext applicationCon ...

  7. spring 源码解析

    1. [文件] spring源码.txt ~ 15B     下载(167) ? 1 springн┤┬вио╬Ш: 2. [文件] spring源码分析之AOP.txt ~ 15KB     下载( ...

  8. Spring源码深度解析

    Spring源码分析 Spring Web入门及源码学习 [Spring源码分析]Bean加载流程概览 Spring Framework 实现原理与源码解析系统 Spring源码分析--水门 Spri ...

  9. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

随机推荐

  1. joda jar日期处理类的学习

    转载:http://www.open-open.com/lib/view/open1348032952724.html 任何企业应用程序都需要处理时间问题.应用程序需要知道当前的时间点和下一个时间点, ...

  2. CFNetwork学习总结

  3. Linux fdisk 命令

    Linux fdisk 命令 fdisk                         fdisk功能说明:磁盘分区.语 法:fdisk [-b <分区大小>][-uv][外围设备代号] ...

  4. PHP strip_tags() 函数

    定义和用法 strip_tags() 函数剥去 HTML.XML 以及 PHP 的标签. 语法 strip_tags(string,allow) 参数 描述 string 必需.规定要检查的字符串. ...

  5. 安装edX DevStack

    概述 edX Developer Stack 就是通常我们所说的Devstack,是为本地开发所设计的一个Vagrant实例. Devstack: 和产品(edx Product Stack)对系统的 ...

  6. 创建安卓app的30个经验教训

    在添加任何第三方party之前,请三思:这真的是一个成熟的项目吗? 如果一个东西用户看不到,就不要绘制它! 除非真的需要,否则别使用数据库: 达到65k方法数限制来的非常快,真的,非常快!不过 mul ...

  7. Android开发中一些被冷落但却很有用的类和方法

    MediaMetadataRetriever 顾名思义,就是用来获取媒体文件一些相关信息的类.包括一首歌的标题,作者,专辑封面和名称,时长,比特率等等.如果是视频的话,可以获取视频的长宽,预览图. h ...

  8. Android自定义属性时format选项可以取用的值

    1. reference:参考某一资源ID. (1)属性定义: <declare-styleable name="名称"> <attr format=" ...

  9. Android 使控件各占屏幕的一半

    在xml中将两个要占屏幕一半的控件都加上android:layout_weight="1": 注意:weight只能用在LinearLayout布局中. 在LinearLayout ...

  10. OpenCV学习笔记:矩阵的掩码操作

    矩阵的掩码操作很简单.其思想是:根据掩码矩阵(也称作核)重新计算图像中每个像素的值.掩码矩阵中的值表示近邻像素值(包括该像素自身的值)对新像素值有多大影响.从数学观点看,我们用自己设置的权值,对像素邻 ...