美文网首页
Spring中Bean名的自定义生成策略

Spring中Bean名的自定义生成策略

作者: wyatt_plus | 来源:发表于2018-06-28 13:45 被阅读0次

    1. 简介

    默认情况下,在spring容器里面的bean name一般都不会出现重复,但是有的时候会因为不同包下的类名默认加载,就会出现bean name重复的情况。

    2. 源码

     * Copyright 2002-2014 the original author or authors.
    
    package org.springframework.context.annotation;
    
    import java.beans.Introspector;
    import java.util.Map;
    import java.util.Set;
    
    import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.StringUtils;
    
    public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    
        private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
    
    
        @Override
        public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
            if (definition instanceof AnnotatedBeanDefinition) {
                String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
                if (StringUtils.hasText(beanName)) {
                    // Explicit bean name found.
                    return beanName;
                }
            }
            // Fallback: generate a unique default bean name.
            return buildDefaultBeanName(definition, registry);
        }
    
        /**
         * Derive a bean name from one of the annotations on the class.
         * @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 = AnnotationConfigUtils.attributesFor(amd, type);
                if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
                    Object value = attributes.get("value");
                    if (value instanceof String) {
                        String strVal = (String) value;
                        if (StringUtils.hasLength(strVal)) {
                            if (beanName != null && !strVal.equals(beanName)) {
                                throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                        "component names: '" + beanName + "' versus '" + strVal + "'");
                            }
                            beanName = strVal;
                        }
                    }
                }
            }
            return beanName;
        }
    
        /**
         * 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"));
        }
    
        /**
         * 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) {
            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);
        }
    
    }
    
    

    3. 解析

    AnnotationBeanNameGenerator是spring的默认生成策略,我们看一眼源码,其中buildDefaultBeanName方法是生成名称的实现,具体如下:

    String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
    return Introspector.decapitalize(shortClassName);
    

    显而易见的,此处通过Bean类名的生成策略。

    这里definition.getBeanClassName()是获取全限定名称的,ClassUtils.getShortName是获取类名的,下面的Introspector.decapitalize实际上就是把首字母变小写的。

    4. 自定义实现

    4.1 创建实现类

    自定义实现类叫MyAnnotationBeanNameGenerator,然后继承AnnotationBeanNameGenerator或者实现BeanNameGenerator接口,之后重写buildDefaultBeanName方法,返回definition.getBeanClassName(),这样我们这个生成策略就写好了。

    4.2 配置spring

    打开applicationContext.xml文件,找到我们配置context:component-scan这块,增加一个参数name-generator=MyAnnotationBeanNameGenerator

    5. 总结

    其实,我们很少会通过这种方式来重写spring bean name 的命名方式,更多的时候我们采用显式的定义bean id来进行设置,而且显式的默认级别更高。我们研究它,更多的能让我们理解spring这个父容器,以及它很多的优秀思想。

    相关文章

      网友评论

          本文标题:Spring中Bean名的自定义生成策略

          本文链接:https://www.haomeiwen.com/subject/ayrgyftx.html