美文网首页
Spring中的beanName

Spring中的beanName

作者: 冰火人生 | 来源:发表于2018-06-06 00:10 被阅读0次

    Spring中的beanName

    在Spring中每一个注册到容器中的Bean都有自己的名字(至少一个),可能不止一个(别名)。对于未明确指定name的Bean,Spring会自动为其生成一个名字。而对于在xml中配置的Bean和使用诸如Service、Component等注解标识的Bean,Spring为其生成名字的方式并不相同,下面我们一一分析。

    核心接口

    核心接口.png

    BeanNameGenerator接口定义如下

    public interface BeanNameGenerator {
    
        /**
         * Generate a bean name for the given bean definition.
         * @param definition the bean definition to generate a name for
         * @param registry the bean definition registry that the given definition
         * is supposed to be registered with
         * @return the generated bean name
         */
        String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
    
    }
    

    BeanNameGenerator是生成beanName的顶级接口,而它有两个实现类,图中左侧的DefaultBeanNameGenerator是给XML配置中Bean使用的,图中右侧的AnnotationBeanNameGenerator则是给通过注解定义的Bean使用的。

    XML配置

    在此不赘述XML文件中Bean的解析过程,直接来看DefaultBeanNameGenerator,其调用链路为

    DefaultBeanNameGenerator#generateBeanName—>BeanDefinitionReaderUtils#generateBeanName

    最后这个方法的定义如下

    public static String generateBeanName(
                BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
                throws BeanDefinitionStoreException {
            // 先拿类名赋值
            String generatedBeanName = definition.getBeanClassName();
            if (generatedBeanName == null) {
                if (definition.getParentName() != null) {
                    generatedBeanName = definition.getParentName() + "$child";
                }
                else if (definition.getFactoryBeanName() != null) {
                    generatedBeanName = definition.getFactoryBeanName() + "$created";
                }
            }
            if (!StringUtils.hasText(generatedBeanName)) {
                throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
                        "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
            }
    
            String id = generatedBeanName;
            if (isInnerBean) {
                // 内部bean,在少数情况下走该分支,例如使用key-ref等标签时
                // Inner bean: generate identity hashcode suffix.
                id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
            }
            else {
                // Top-level bean: use plain class name.
                // Increase counter until the id is unique.
                // 为了保证id唯一,在其后加数字
                int counter = -1;
                while (counter == -1 || registry.containsBeanDefinition(id)) {
                    counter++;
                    id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
                }
            }
            return id;
        }
    

    注释都写在了上面,逻辑很简单:类名+“#”+数字

    注解配置

    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
            if (definition instanceof AnnotatedBeanDefinition) {
                // 如果注解的value指定了beanName,则使用该值
                String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
                if (StringUtils.hasText(beanName)) {
                    // Explicit bean name found.
                    return beanName;
                }
            }
            // Fallback: generate a unique default bean name.
            // 如果没有指定value,则为其生成beanName
            return buildDefaultBeanName(definition, registry);
        }
    

    继续追踪

    protected String buildDefaultBeanName(BeanDefinition definition) {
            String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
            return Introspector.decapitalize(shortClassName);
        }
    

    Introspector.decapitalize的代码如下

    public static String decapitalize(String name) {
            if (name == null || name.length() == 0) {
                return name;
            }
            if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                            Character.isUpperCase(name.charAt(0))){
                return name;
            }
            char chars[] = name.toCharArray();
            chars[0] = Character.toLowerCase(chars[0]);
            return new String(chars);
        }
    

    通过上面两段代码可以看出逻辑如下

    1. 取短类名,即不包含包路径的类名,例如com.test.Student的短类名为Student,这点跟XML配置中取全类名不一样
    2. 如果短类名长度大于1,且第一个和第二个字符为大写,则直接返回短类名,也就是说假设类为com.test.STudent,则beanName为STudent
    3. 其他情况下将短类名首字符小写后返回,假设类为com.test.Student,则beanName为student

    验证

    由于只为了验证beanName,简单起见,Bean类中都为空

    People类

    @Component
    public class Pepole {
    }
    

    TNtt类

    @Service
    public class TNttt {
    }
    

    TestPepole类

    public class TestPepole {
    }
    

    TNTt类

    public class TNTt {
    }
    

    其中TestPepole和TNTt通过XML配置

    <bean class="com.hust.TestPepole"></bean>
        <bean class="com.hust.TNTt"></bean>
    

    测试主类

    public class App {
        public static void main(String[] args) throws IOException {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");
            System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Pepole.class)));
            System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
                    TNttt.class)));
            System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
                    TestPepole.class)));
            System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
                    TNTt.class)));
        }
    }
    

    输出结果

    ["pepole"]
    ["TNttt"]
    ["com.hust.TestPepole#0"]
    ["com.hust.TNTt#0"]
    

    总结

    • 在不指定beanName的情况下,Spring会自动为注册的Bean生成一个唯一的beanName
    • 通过注解注册的Bean和XML注册的Bean,Spring为其生成默认beanName的机制不一样
    • 不要盲目觉得通过注解注册的Bean,Spring为其生成beanName就是将短类名的首字母小写,当短类名的首字符和第二个字符均大写时,beanName就是短类名

    相关文章

      网友评论

          本文标题:Spring中的beanName

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