Spring源码入门——AnnotationBeanNameGenerator解析

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

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

来源: http://www.cnblogs.com/jason0529/p/5274650.html

来自为知笔记(Wiz)

时间: 2025-01-11 18:40:44

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

Spring源码入门——XmlBeanDefinitionReader解析

接上篇[] ,我们看到BeanDefinitionReader解决的是从资源文件(xml,propert)到BeanDefinition集合的过程.所以BeanDefinitionReader接口有两个实现版本. BeanDefinitionReader的接口声明,ResourceLoader是spring中解决Resource加载的操作.四个loadBeanDefinitions就是重载解决单个或者多个资源文件的处理问题. loadBeanDefinitions 是加载BeanDefiniti

Spring源码入门——DefaultBeanNameGenerator解析

我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指定其id和name值,但那些没有指定的,或者注解的spring的beanname怎么来的的?就是BeanNameGenerator接口实现的特性. <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><prop

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge

spring源码剖析(二)Spring默认标签解析及注册实现

在使用spring的时候,我也经常会使用到bean标签,beans标签,import标签,aop标签等. 下面主要为读者介绍spring的默认的自带标签的解析流程. 验证模式(DTD&XSD) dtd基本已被淘汰,现在spring的验证模式基本都是采用xsd文件作为xml文档的验证模式,通过xsd文件可以检查该xml是否符合规范,是否有效.在使用xsd文件对xml文档进行校验的时候,除了要名称空间外(xmlns="http://www.springframework.org/schema

spring源码(3)之解析配置文件的过程

spring源码之解析配置文件过程 上篇博文,我们探讨了spring获取配置文件applicationContext.xml的document对象.回想struct2解析struct*.xml,当struct2获取struct*.xml文件的document对象之后,就会循环遍历这个document,然把不同的标签的信息封装到不同的对象中,如<package>标签封装到packageConfig对象,<action>标签封装到actionConfig对象等等.那么spring在获取

Spring源码深度解析第一天

其实第一天已经过去了,今天是第二天.iteye刚注册的小号就被封了.不论是它的失误还是他的失误总之我跟iteye是没有缘分了. 昨天基本没有进展.所以从今天开始说了.下面流水账开始了. <Spring源码深度解析>这本书没有pdf完整版是让我很失望的.如果有完整版即使看完了我也会选择买一本实体如果有用的话. 书中说从github下载源码.发现github没有想象中的简单易懂.还需要记忆很多命令才能玩得转.从github上获得了Spring源码后需要使用Gradle来编译成eclipse项目.g

SPRING技术内幕,Spring源码深度解析

 SPRING技术内幕,Spring源码深度解析 SPRING技术内幕:深入解析SPRING架构与设计原理(第2版)[带书签].pdf: http://www.t00y.com/file/78131650 Spring源码深度解析 [郝佳编著] sample.pdf: http://www.t00y.com/file/78131634 [jingshuishenliu.400gb.com]Spring Data.pdf: http://www.t00y.com/file/78256084 [

Spring 源码解析之ViewResolver源码解析(四)

Spring 源码解析之ViewResolver源码解析(四) 1 ViewResolver类功能解析 1.1 ViewResolver Interface to be implemented by objects that can resolve views by name. View state doesn't change during the running of the application, so implementations are free to cache views. I

软件开发工程师(JAVA)中级考试大纲--spring源码解析

spring源码解析(1)----IOC 一.IOC容器 在Spring中,IOC容器的重要地位我们就不多说了,对于Spring的使用者而言,IOC容器实际上是什么呢?我们可以说BeanFactory就是我们看到的IoC容器,当然了Spring为我们准备了许多种IoC容器来使用,这样可以方便我们从不同的层面,不同的资源位置,不同的形式的定义信息来建立我们需要的IoC容器. 在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定