前言
众所周知,Spring容器可以简单粗暴的把它理解为一个大大的Map,存储着容器所管理的所有的单实例对象。我们从使用getBean(String beanName)方法,根据bean名称就能获得容器内唯一的Bean实例就能“证明”到这一点。
可你是否曾想过:既然它是Map,那万一我们写的@Bean的beanName重名了怎么办呢?Spring框架是怎么来处理这个事的呢?
Spring容器通俗描述
我们把它理解成一个Map,那Map里面的key-value你应该知道:
- key:beanName
- value:单例bean对象
其实为了辅助理解,从SingletonBeanRegistry注册Bean的方法中也可以看出:
public interface SingletonBeanRegistry { ... // ===注意它没有remove方法==== void registerSingleton(String beanName, Object singletonObject); Object getSingleton(String beanName); boolean containsSingleton(String beanName); int getSingletonCount(); ... }
同样的bean定义信息的注册器BeanDefinitionRegistry也能类比的看到:
public interface BeanDefinitionRegistry extends AliasRegistry { ... void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; // 请注意:SingletonBeanRegistry 可没有移除方法~ void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; boolean containsBeanDefinition(String beanName); int getBeanDefinitionCount(); ... }
在我们现在的Spring应用中,动不动容器就需要管理上千个Bean(更有甚者在过去的one in all应用中可能出现上万个Bean的情况)。
既然Spring容器是个Map,那key的重要性不言而喻,他指向着全局唯一的Bean实例,若key被覆盖了,就相当于Map的key被覆盖一样,旧的value值可能将永远就触达不到了~
从而可见,确保beanName的唯一性意义重大。但是呢管理的Bean多了,怎么去确保这件事肯定就成了一个难题,那么接下来就了解一下Spring它是怎么造的~
beanName的生成规则
我把beanName的生成规则放在最开始描述,是因为我觉得既然涉及到beanName,那么首先就应该知道beanName是怎么来的。
我们知道,Spring提供了非常非常多的方式允许我们向容器内注册一个Bean,下面总结出常用的注册Bean方式对应的BeanName如下:
- xml的<bean/>标签方式,由id属性决定(若没指定则为全类名)
- @Component模式注解方式(包含其所有派生注解如@Service、@Configuration等等)。指定了value值就是value值,否则是类名首字母小写1. 此种方式是最为常用,也是大批量注册Bean的首选方式
- @Bean方式。指定了value值就是它,否则就是方法名
- FactoryBean方式。(它其实需要结合上面任意一种方式使用,beanName请参考如上描述)
- …
这是一个基本的结论。其实大多数时候我们自己都并不会去指定beanName,若没有自己没有指定的话,它怎么来的呢?Spring对它的生成有什么规律可循呢?那么接下来就就研究下这个策略:名称生成策略
BeanNameGenerator
为bean定义生成bean名称的策略接口。
BeanNameGenerator接口位于 org.springframework.beans.factory.support 包下面,只声明了一个方法,接受两个参数:definition 被生成名字的BeanDefinition实例;registry 生成名字后注册进的BeanDefinitionRegistry。
// @since 2.0.3 是2.0后出来的 public interface BeanNameGenerator { // 入参竟然有两个 definition和bean定义注册器 String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry); }
看看它的继承树:
BeanNameGenerator有两个实现版本,DefaultBeanNameGenerator和AnnotationBeanNameGenerator。其中DefaultBeanNameGenerator是给资源文件加载bean时使用(BeanDefinitionReader中使用);AnnotationBeanNameGenerator是为了处理注解生成bean name的情况。
DefaultBeanNameGenerator
它是用来处理xml资源文件的Bean name生成器
// @since 2.0.3 public class DefaultBeanNameGenerator implements BeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { // isInnerBean 如果是内部类表示true,这个工具类也能处理 return BeanDefinitionReaderUtils.generateBeanName(definition, registry); } }
将具体的处理方式委托给了BeanDefinitionReaderUtils.generateBeanName
这个方法来处理:
public abstract class BeanDefinitionReaderUtils { // unique, "#1", "#2" etc will be appended, until the name becomes public static final String GENERATED_BEAN_NAME_SEPARATOR = BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR; // isInnerBean:是为了区分内部bean(innerBean)和顶级bean(top-level bean). public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { // 拿到Bean定义信息里面的BeanClassName全类名 // 注意这个不是必须的,因为如果是继承关系,配上父类的依旧行了 String generatedBeanName = definition.getBeanClassName(); if (generatedBeanName == null) { // 若没有配置本类全类名,去拿到父类的全类名+$child"俩表示自己 if (definition.getParentName() != null) { generatedBeanName = definition.getParentName() + "$child"; } // 工厂Bean的 就用方法的名字+"$created" 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"); } //isInnerBean=true表示你是内部类的话,名字又增加了如下变化 String id = generatedBeanName; if (isInnerBean) { // Inner bean: generate identity hashcode suffix. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); } // 如果不是内部类(绝大多数情况下都如此) // 此方法注意:一定能够保证到你的BeanName是唯一的~~~~ else { // Top-level bean: use plain class name with unique suffix if necessary. // Top-level表示最外层的Bean,也就是说非内部类 这里生成绝对唯一的BeanName~~~~ return uniqueBeanName(generatedBeanName, registry); } return id; } public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) { String id = beanName; int counter = -1; // Increase counter until the id is unique. while (counter == -1 || registry.containsBeanDefinition(id)) { counter++; id = beanName + GENERATED_BEAN_NAME_SEPARATOR + counter; } return id; } }
对于它的处理逻辑,可以总结为如下步骤:
- 读取待生成Bean实例的类名称(未必是运行时的实际类型)。
- 如果类型为空,则判断是否存在parent bean,如果存在,读取parent bean的name + “$child”。
- 如果parent bean 为空,那么判断是否存在factory bean ,如存在,factory bean name + “$created”。 到此处前缀生成完毕
- 如果前缀为空,直接抛出异常,没有可以定义这个bean的任何依据。
- 前缀存在,判断是否为内部bean(innerBean,此处默认为false),如果是,最终为前缀+分隔符+十六进制的hashcode码
- 如果是顶级bean(top-level bean ),则判断前缀+数字的bean是否已存在,循环查询,知道查询到没有使用的id为止。处理完成(所以这个生成器肯定能保证Bean定义的唯一性,不会出现Bean name覆盖问题)
需要注意的是,DefaultBeanNameGenerator在Spring中已经几乎处于一个被弃用了的状态,唯一使用地方为
// @since 11.12.2003 public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable { private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) { this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new DefaultBeanNameGenerator()); } }
而看看这个类的实现类们:
显然我们现在几乎不会再使用XmlBeanDefinitionReader,所以粗暴的可以理解为:此名称生成器已经废弃~
后来想了想其实这句话这么说不妥,毕竟我们使用@ImportResource的时候还是会导入xml文件进来的,因此各位自己感受吧
AnnotationBeanNameGenerator
javadoc的描述:它能够处理@Component以及它所有的派生注解,并且还支持JavaEE的javax.annotation.@ManagedBean、以及JSR 330的javax.inject.@Named注解。如果注解不指定bean名称,则将基于类的短名称(小写的第一个字母)生成适当的名称。
// @since 2.5 它的出现是伴随着@Component出现 public class AnnotationBeanNameGenerator implements BeanNameGenerator { // 支持的最基本的注解(包含其派生注解) private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component"; @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { // //判断是否是否是AnnotatedBeanDefinition的子类, AnnotatedBeanDefinition是BeanDefinition的一个子类 // 显然这个生成器只为AnnotatedBeanDefinition它来自动生成名称 if (definition instanceof AnnotatedBeanDefinition) { // determineBeanNameFromAnnotation这个方法简而言之,就是看你的注解有没有标注value值,若指定了就以指定的为准 // 支持的所有注解:上面已经说明了~~~ // 此处若配置了多个注解且都指定了value值,但发现value值有不同的,就抛出异常了~~~~~ 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); } // 它的方法是protected 由此可见若我们想自定义生成器的话 可以继承它 然后复写 protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return buildDefaultBeanName(definition); } // 这里是先拿到ClassUtils.getShortName 短名称 protected String buildDefaultBeanName(BeanDefinition definition) { String beanClassName = definition.getBeanClassName(); Assert.state(beanClassName != null, "No bean class name set"); String shortClassName = ClassUtils.getShortName(beanClassName); // 调用java.beans.Introspector的方法 首字母小写 return Introspector.decapitalize(shortClassName); } }
对于它的处理逻辑,可以总结为如下步骤:
- 读取所有注解类型
- 遍历所有注解类型,找到所有为Component等所有支持的含有非空value属性的注解
- fallback到自己生成beanName
在注解大行其道的今天,AnnotationBeanNameGenerator的使用场景就非常非常之多了,整理如下:
AnnotatedBeanDefinitionReader
public class AnnotatedBeanDefinitionReader { ... private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); ... }
bean扫描器ClassPathBeanDefinitionScanner
中会使用到AnnotatedBeanDefinitionReader
去读取Bean定义信息们~