一.BeanDefinition加载时机
bean的创建是根据BeanDefinition来的,那BeanDefinition 何时初始化的。
BeanDefinition 信息集合在 DefaultListableBeanFactory 中维护,看一下哪些方法对 BeanDefinition进行了修改,然后打断点测试。
主要是 registerBeanDefinition 方法,注册和修改bean定义。
配置类
@Configuration
public class MainConfigTiDaiBean {
@Bean(name = "1")
public Object a(){
return new Object();
}
@Bean(name = "1")
public Object b(){
return new Object();
}
}
测试类
@Test
public void test1(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigTiDaiBean.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String bean:beanDefinitionNames){
System.out.println(bean);
}
applicationContext.close();
}
先是spring自带的一些bean 定义加载,是在容器的 AnnotationConfigApplicationContext 第一步this方法中执行的;
我们自定义config配置类 的bean定义加载,则是在 AnnotationConfigApplicationContext 第二步 register(annotatedClasses)执行。 会对bean定义集合增加。
那配置类中的 @Bean注释的方法呢? 是容器第三步 refresh中执行,对bean定义集合修改。
通过 BeanFactoryPostProcessors 对bean定义进行修改
二.同一个配置类中@Bean 中bean名字重复
配置类中两个bean name一样为1,结果只会加载第一个bean。
@Configuration
public class MainConfigTiDaiBean {
@Bean(name = "1")
public Object a(){
return new Object();
}
@Bean(name = "1")
public Object b(){
return new Object();
}
}
顺着registerBeanDefinition debug,有一步是根据 配置类中的bean方法去加载bean定义
第一个方法 a ,会加载 name为1的bean定义,把断点调试到 第二个方法 b,
其中会判断bean定义是否已经存在
逻辑是:拿着bean name 去已经存在的bean定义集合中寻找,如果找到了bean定义,则看旧bean定义所在的类,是否和新bean定义所在类相同,相同则返回,不再加载新的bean定义。
意思就是一个类中不能有两个相同的bean name。
三.不同配置类的bean名字重复
再加一个配置类,@bean 名字也为1,结果还是只有一个bean被创建。
结论:后加载的 重名beanDefination 会替代先加载的beanDefination。
@Configuration
public class MainConfigTiDaiBean2 {
@Bean(name = "1")
public Object a(){
return new Object();
}
}
核心在 DefaultListableBeanFactory 类的 registerBeanDefinition 方法。
通过当前的bean名字找到 已经存在的 旧bean定义。
旧bean定义如果不为空,就会用新bean定义覆盖旧bean定义。bean定义信息存放的集合是 beanDefinitionMap ,beanDefinitionNames 只是存放了名字。
所以后续加载的 重名bean 会替代先加载的bean。
会对旧的bean定义进行清除,如果旧bean定义已经产生了实例,可以清空等操作。