我们知道,
BeanFactory的getBean(String name)方法可以获取指定名称的Bean
其中,name参数既可以是Bean的名称,也可以是Bean的别名
因此,在解析BeanFactory源码之前,有必要先了解一下Bean的名称和别名
另外,本系列文章中的源码,如果没有特殊说明,都以spring-xxx-5.3.8为准
1. Xml文件配置Bean
1.1. 概述
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
当同时配置了id和name属性时,id值将作为Bean的名称,而name值将作为Bean的别名
另外,name属性可以配置多个名称(用逗号或分号或空格隔开),此时这些名称都将作为该Bean的别名
-->
<bean id="obj1Id" name="obj1Name" class="java.lang.Object" />
<!--
当只配置了name属性时,name值就是Bean的名称,并且没有别名
另外,name属性可以配置多个名称(用逗号或分号或空格隔开),此时第一个名称为Bean的名称,之后的都是别名
-->
<bean name="obj2Name" class="java.lang.Object" />
<!--
当id和name属性都没有配置时,Bean名称的生成规则是:全限定类名 + "#" + 下标
另外,全限定类名还会作为第一个无id和name的该类型的Bean的别名
-->
<bean class="java.lang.Object" /> <!-- Bean名称为"java.lang.Object#0",同时还有一个别名"java.lang.Object" -->
<bean class="java.lang.Object" /> <!-- Bean名称为"java.lang.Object#1" -->
<!-- 也可以通过alias标签给Bean名称或Bean别名再起一个别名 -->
<alias name="obj3Id" alias="obj3IdAlias" />
<alias name="obj3Name" alias="obj3NameAlias" />
<alias name="obj3NameAlias" alias="obj3NameAliasAlias" />
<bean id="obj3Id" name="obj3Name" class="java.lang.Object" />
</beans>
测试程序如下:
public class BootApiApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
System.out.println(ctx.getBean("obj1Id"));
System.out.println(ctx.getBean("obj1Name"));
System.out.println(ctx.getBean("obj2Name"));
System.out.println(ctx.getBean("obj3Id"));
System.out.println(ctx.getBean("obj3IdAlias"));
System.out.println(ctx.getBean("obj3Name"));
System.out.println(ctx.getBean("obj3NameAlias"));
System.out.println(ctx.getBean("obj3NameAliasAlias"));
System.out.println(ctx.getBean("java.lang.Object"));
System.out.println(ctx.getBean("java.lang.Object#0"));
System.out.println(ctx.getBean("java.lang.Object#1"));
}
}
ClassPathXmlApplicationContext底层的BeanFactory的一级缓存中的所有的Bean名称如下(这里省略掉其它无关的Bean名称):
[
"obj1Id",
"obj3Id",
"java.lang.Object#1",
"java.lang.Object#0",
"obj2Name"
]
BeanFactory的aliasMap(用于存放别名和原名的对应关系)内容如下:
{
"obj3IdAlias": "obj3Id",
"java.lang.Object": "java.lang.Object#0",
"obj1Name": "obj1Id",
"obj3NameAliasAlias": "obj3NameAlias",
"obj3Name": "obj3Id",
"obj3NameAlias": "obj3Name"
}
1.2. 源码解析
/**
* 解析Spring配置文件时,底层是通过本类来进行解析的
*/
public class BeanDefinitionParserDelegate {
/**
* bean标签的name属性值可以指定多个名称,每个名称可以用逗号、分号或空格隔开
*/
public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
/**
* 解析bean标签(即这里的ele参数),并返回解析到的BeanDefinition的Holder
*
* @param containingBean 目前只需将该参数认为是null即可
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取到id属性值和name属性值(注意name属性值可以指定多个名称)
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 如果指定了name属性,则对name属性值进行切分,获取到各个名称;这些名称都默认看作是别名
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// id值默认看作是Bean名称
String beanName = id;
// 如果没有指定id属性,但指定了name属性,则将name属性值中第一个名称作为Bean名称,剩下的作为别名
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
// 先不管这段代码
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 解析bean标签的其它属性,并获取到相应的BeanDefinition;具体的解析过程不用去管
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果此时beanName没有数据,说明没有指定id属性和name属性
if (!StringUtils.hasText(beanName)) {
try {
// 这个分支不用管
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
// 直接看这个分支
} else {
// 重新生成Bean名称(如"java.lang.Object#0")
beanName = this.readerContext.generateBeanName(beanDefinition);
// 如果当前Bean名称以全限定类名作为前缀,且全限定类名还没有作为其它Bean的名称或别名
// 则将全限定类名作为当前Bean的别名添加到aliases集合中
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// 根据Bean名称和别名创建并返回相应的BeanDefinitionHolder
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
/**
* BeanDefinitionReaderUtils类中的方法
* this.readerContext.generateBeanName(beanDefinition)最终会执行到这里
*
* @param beanName 这里是Bean所属类的全限定名称
*/
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
String id = beanName;
int counter = -1;
// Bean名称的前缀为:所属类全限定名称 + "#"
String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
// 后缀数字从0开始;如果Bean名称(前缀+后缀)没被注册,则返回该Bean名称;否则后缀数字加一
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = prefix + counter;
}
return id;
}
}
2. @Bean注解配置Bean
2.1. 概述
@Configuration
public class Config {
/**
* 当@Bean注解没有指定Bean名称时,方法名将作为Bean的名称
*/
@Bean
public Object obj1() {
return new Object();
}
/**
* 当@Bean注解指定了一个Bean名称时,该名称将作为Bean的名称
*/
@Bean("obj2")
public Object anyMethodName1() {
return new Object();
}
/**
* 当@Bean注解指定了多个Bean名称时,第一个名称将作为Bean的名称,剩下的名称将作为第一个名称的别名
*/
@Bean({"obj3", "obj3Alias1", "obj3Alias2"})
public Object anyMethodName2() {
return new Object();
}
}
根据该配置类创建AnnotationConfigApplicationContext组件,其底层的BeanFactory的一级缓存中的Bean名称和aliasMap如下:
[
"obj2",
"obj1",
"obj3",
"config"
]
{
"obj3Alias2": "obj3",
"obj3Alias1": "obj3"
}
2.2. 源码解析
class ConfigurationClassBeanDefinitionReader {
/**
* 解析@Bean方法(即这里的beanMethod参数)
*/
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// 和@Conditional相关的判断,确保当前Bean确实需要注册;具体的逻辑不用管
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
// 获取@Bean注解中的所有属性
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
// 获取name属性的值(该值可以包含多个名称)
// 注意,@Bean注解的name属性和value属性互为别名,因此可以通过name属性获取到value属性的值
// 另外还要注意,name和value两个属性不能同时配置;如果同时配置,则值必须相同,否则会抛异常
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
// 如果指定了name属性值,则第一个名称即为Bean名称(并将第一个名称从names集合中移除);否则Bean名称为方法名称
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// names中保存的是该Bean的别名,因此注册这些别名
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// 之后的代码省略
}
}
3. @Component注解配置Bean
3.1. 概述
/**
* 当@Component注解未指定名称时,Bean名称默认为类名(首字母小写);如:TestBean.class -> "testBean"
* 特别地,如果类名前两个字符都是大写,则Bean名称是类名本身;如:XMLParser.class -> "XMLParser"
*/
@Component
public class TestBean {
}
/**
* 当@Component注解指定了名称时,则该名称即为Bean名称
* 注意,这里的名称不支持多个,如果名称填的是"name,alias",则Bean名称就是"name,alias"
*/
@Component("testBeanName")
public class TestBean {
}
3.2. 源码解析
public class AnnotationBeanNameGenerator implements BeanNameGenerator {
/**
* 为Bean生成对应的名称
*/
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
// 先尝试从@Componnet等注解中获取value值,如果能获取到,则直接返回该值
// 注意,如果有多个注解,且注解的value值不相同(如@Component("a")和@Service("b")),则这里会报错
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
return beanName;
}
}
// 否则,生成一个默认的Bean名称
return buildDefaultBeanName(definition, registry);
}
/**
* 当没有指定@Component等注解的value值时,调用该方法获取Bean的名称
*/
protected String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
Assert.state(beanClassName != null, "No bean class name set");
// 获取类名,然后将首字母转成小写并返回
String shortClassName = ClassUtils.getShortName(beanClassName);
return Introspector.decapitalize(shortClassName);
}
/**
* Introspector中的方法,负责将name中第一个字符转成小写形式
*/
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);
}
}
4. FeignClient的名称和别名
/**
* FeignClient的全限定类名将作为Bean的名称,而注解的name/value属性值拼接上"FeignClient"将作为该Bean的别名
* 比如,该Bean的名称是"com.xxx.ZtxyFeignClient",别名是"ztxy123FeignClient"
*/
@FeignClient(name = "ztxy123", url = "${ztxy.host}", path = "/path")
public interface ZtxyFeignClient {
}
5. FactoryBean相关
5.1. 概述
/**
* 自定义一个FactoryBean
*/
public class LongFactoryBean implements FactoryBean<Long> {
@Override
public Long getObject() throws Exception {
return 233L;
}
@Override
public Class<?> getObjectType() {
return Long.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 注册该FactoryBean;注意,在一级缓存中,"longFactoryBean"名称对应的是FactoryBean本身,而不是其生产的Long对象 -->
<bean id="longFactoryBean" name="longFactoryBeanName1;longFactoryBeanName2" class="com.xxx.LongFactoryBean" />
</beans>
public class BootApiApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
// 虽然"longFactoryBean"这个名称对应的是FactoryBean
// 但工厂发现拿到的Bean是FactoryBean类型的,于是会将该FactoryBean生产的Bean返回
System.out.println(ctx.getBean("longFactoryBean"));
// 如果要想获取FactoryBean本身,可以在Bean名称前面加上"&"前缀(可以加多个,效果是一样的)
// 工厂在获取Bean时,会先去掉所有的"&"前缀,再进行查找,从而得到FactoryBean本身
// 然后工厂发现用户指定的Bean名称是以"&"开头的,因此会直接返回FactoryBean,而不是返回其生产的Bean
System.out.println(ctx.getBean("&longFactoryBean"));
System.out.println(ctx.getBean("&&longFactoryBeanName1"));
System.out.println(ctx.getBean("&&&longFactoryBeanName2"));
}
}
5.2. doGetBean()
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
/**
* BeanFactory.getBean(name)方法最终会调用本方法,此时后面三个参数分别是null、null、false
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 将用户指定的Bean名称中的"&"前缀去掉,并且将别名转成Bean名称
String beanName = transformedBeanName(name);
// 最终要返回的Bean实例
Object beanInstance;
// 调用getObjectForBeanInstance()方法处理xxxInstance,该方法的返回值是真正要返回的Bean
// 这里的xxxInstance代表在容器内部找到的Bean实例;先不用关心xxxInstance是怎么获取到的
beanInstance = getObjectForBeanInstance(xxxInstance, name, beanName, mbd);
// getObjectForBeanInstance()方法的主要逻辑:
// 1. 判断用户是否想获取FactoryBean(name是否以"&"开头)
// 2. 如果是,则直接返回xxxInstance(此时该实例必须是FactoryBean类型)
// 3. 否则,如果xxxInstance是FactoryBean,则返回xxxInstance生产的Bean,否则还是返回xxxInstance
// 进行类型转换并返回;由于这里requiredType是null,因此这里会直接将beanInstance转成返回值的类型
return adaptBeanInstance(name, beanInstance, requiredType);
}
}
5.3. transformedBeanName()
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
/**
* 将用户指定的Bean名称转成BeanFactory能识别的Bean名称
*/
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
/**
* BeanFactoryUtils中的方法,负责将name中的"&"前缀去掉
*/
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
// 去掉name的"&"前缀,然后将结果返回(这里还会将结果缓存起来)
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
} while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
/**
* 继承自SimpleAliasRegistry的方法;将Bean的别名转成Bean的名称
*/
public String canonicalName(String name) {
// 先假设name就是Bean名称
String canonicalName = name;
String resolvedName;
do {
// 根据canonicalName在aliasMap中查找对应的值
// 如果找得到,说明canonicalName是别名,因此将canonicalName更新为它所对应的原名称,然后继续循环判断
// 否则,说明canonicalName就是Bean名称,此时返回该canonicalName
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
} while (resolvedName != null);
return canonicalName;
}
}
5.4. getObjectForBeanInstance()
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
/**
* 处理和FactoryBean相关的逻辑
*
* @param name 用户指定的Bean名称
* @param beanName 真正的Bean名称
* @param beanInstance beanName对应的Bean
*/
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 如果name以"&"为前缀,说明用户想要获取FactoryBean
// 此时要求beanInstance是FactoryBean类型,然后直接返回beanInstance即可
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// 否则,说明用户想要获取某个具体的Bean
// 如果beanInstance不是FactoryBean类型,则直接返回该实例即可
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
// 否则,说明需要将FactoryBean生产的Bean返回
// 先查询factoryBeanObjectCache缓存(该缓存会保存FactoryBean生产的单例Bean)
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
} else {
object = getCachedObjectForFactoryBean(beanName);
}
// 如果缓存没有找到,则调用getObjectFromFactoryBean()方法来获取目标Bean;主要逻辑为:
// 1. 调用FactoryBean的getObject()方法获取目标Bean
// 2. 如果发现目标Bean是单例的,则将其放入factoryBeanObjectCache缓存
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
}