前言
在之前的文章中,我们已经对 bean
的准备工作进行了讲解,包括 bean
定义和 FactoryBean
判断等。在这个基础上,我们可以更加深入地理解 getBean
方法的实现逻辑,并在后续的学习中更好地掌握createBean
方法的实现细节。
getBean用法
讲解getBean方法之前,我们先来看看他有几种常见的用法:
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService bean1 = applicationContext.getBean(UserService.class);
UserService bean2 = (UserService)applicationContext.getBean("userService");
UserService bean3 = applicationContext.getBean("userService",UserService.class);
UserService bean4 = (UserService) applicationContext.getBean("userService",new OrderService());
bean1.test();
bean2.test();
bean3.test();
bean4.test();
关于获取 bean
的方法,前两种方法应该比较常见,这里就不再赘述。第三种方法实际上是在获取 bean
的时候,会先判断是否符合指定的类型,如果符合,则进行类型转换并返回对应的 bean
实例。第四种方法则是在创建 bean
实例时,通过推断构造方法的方式来选择使用带有参数的构造方法进行实例化。
如果我们想要让第四种方法生效,可以考虑使用多例的形式,即通过设置 scope
属性为 prototype
来实现。这样,每次获取 bean
时,都会创建新的 bean
实例,从而可以触发使用带有参数的构造方法进行实例化,比如这样:
@Component
@Scope("prototype")
public class UserService {
public UserService(){
System.out.println(0);
}
public UserService(OrderService orderService){
System.out.println(1);
}
public void test(){
System.out.println(11);
}
}
getBean大体流程
由于方法代码太多,我就不贴代码了,我这边只贴一些主要的伪代码,方便大家阅读,然后我在对每个流程细讲下:
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// name有可能是 &xxx 或者 xxx,如果name是&xxx,那么beanName就是xxx
// name有可能传入进来的是别名,那么beanName就是id
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 如果sharedInstance是FactoryBean,那么就调用getObject()返回对象
}
else {
//检查是否本beanfactory没有当前bean定义,查看有父容器,如果有,则调用父容器的getbean方法
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 检查BeanDefinition是不是Abstract的
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
//查看是否有dependsOn注解,如果存在循环依赖则报错
// Create bean instance.
if (mbd.isSingleton()) {
//调用createBean方法
//如果是FactoryBean则调用getObject
}
else if (mbd.isPrototype()) {
//调用createBean方法,与单例只是前后逻辑不一样
//如果是FactoryBean则调用getObject
}
else {
Scope不同类型有不同实现
//调用createBean方法,与单例只是前后逻辑不一样
//如果是FactoryBean则调用getObject
}
}catch{
......
}
}
// 检查通过name所获得到的beanInstance的类型是否是requiredType
return adaptBeanInstance(name, beanInstance, requiredType);
}
单例缓存池
在 Spring 中,不管传入的 beanName 是多例的还是单例的,都会先从单例缓存池中获取。有些人可能会觉得这样做会浪费一些性能,但实际上 Spring 考虑到了大部分托管的 bean 都是单例的情况,因此忽略了这一点性能。实际上,这样的性能消耗并不大。可以将其类比于 Java 的双亲委派机制,都会先查看本加载器是否有缓存,如果没有再向父加载器去加载。
parentBeanFactory
在分析 bean 定义是如何创建的时,我们可以不考虑单例缓存池中获取对象的情况,而是逐步分析 bean 定义是如何创建的。在这个过程中,即使存在 parentBeanFactory,我们也可以跳过它,因为我们的启动容器并没有设置任何父容器。源码也很简单,如果本容器没有 bean 定义,就直接调用父容器的 getBean 相关方法:
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null &a