关于Spring IoC bean 的加载问题讨论

前言本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。本篇文章主要介绍 Spring IoC 容器是怎么加载 bean 的。正文我们先看一下Spring IoC BeanDefinition 的加载和注册一文中获取 bean 的实例代码:Copypublic class BeanDefinitionDemo {public static void main(String[] args) {
摘要由CSDN通过智能技术生成

前言

本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。

本篇文章主要介绍 Spring IoC 容器是怎么加载 bean 的。
正文

我们先看一下Spring IoC BeanDefinition 的加载和注册一文中获取 bean 的实例代码:

Copy
public class BeanDefinitionDemo {

public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("META-INF/bean-definition.xml");
    User user = beanFactory.getBean("user", User.class);
    System.err.println(user);
}

}

通过 beanFactory.getBean() 这个方法就获取了在 XML 中定义的 bean,下面我们就重点分析这个方法背后做了什么操作。

在正式开始之前,我们先了解一下 FactoryBean 及其用法。
FactoryBean 介绍#

FactoryBean 接口对于 Spring 框架来说占有重要的地位,Spring 自身就提供了70多个 FactoryBean 的实现。它们隐藏了一下复杂 bean 的细节,给上层应用带来了便利。下面是该接口的定义:

Copy
public interface FactoryBean {

// 返回由FactoryBean创建的bean实例,如果isSingleton()返回true,
// 则该实例会放到Spring容器中单例缓存池中
@Nullable
T getObject() throws Exception;

// 返回FactoryBean创建的bean类型
@Nullable
Class<?> getObjectType();

// 返回由FactoryBean创建的bean实例的作用域是singleton还是prototype
default boolean isSingleton() {
    return true;
}

}

当配置文件中 的 class 属性配置的实现类时 FactoryBean 时,通过 getBean() 返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 所返回的对象,相当于 FactoryBean#getObject() 代理了 getBean()。下面用简单的代码演示一下:

首先定义一个 Car 实体类:

Copy
public class Car {

private Integer maxSpeed;
private String brand;
private Double price;

public Integer getMaxSpeed() {
    return maxSpeed;
}

public void setMaxSpeed(Integer maxSpeed) {
    this.maxSpeed = maxSpeed;
}

public String getBrand() {
    return brand;
}

public void setBrand(String brand) {
    this.brand = brand;
}

public Double getPrice() {
    return price;
}

public void setPrice(Double price) {
    this.price = price;
}

}

上面的实体类,如果用传统方式配置,每一个属性都会对应一个 元素标签。如果用 FactoryBean 的方式实现就会灵活一点,下面通过逗号分隔的方式一次性的为 Car 的所有属性配置值。

Copy
public class CarFactoryBean implements FactoryBean {

private String carInfo;

@Override
public Car getObject() throws Exception {
    Car car = new Car();
    String[] infos = carInfo.split(",");
    car.setBrand(infos[0]);
    car.setMaxSpeed(Integer.valueOf(infos[1]));
    car.setPrice(Double.valueOf(infos[2]));
    return car;
}

@Override
public Class<?> getObjectType() {
    return Car.class;
}

@Override
public boolean isSingleton() {
    return true;
}

public String getCarInfo() {
    return carInfo;
}

public void setCarInfo(String carInfo) {
    this.carInfo = carInfo;
}

}

接下来,我们在 XML 中配置。

Copy


最后看下测试代码和运行结果:

Copy
@Test
public void factoryBeanTest() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(“META-INF/factory-bean.xml”);
Car car = beanFactory.getBean(“car”, Car.class);
System.out.println(car);
CarFactoryBean carFactoryBean = beanFactory.getBean("&car", CarFactoryBean.class);
System.out.println(carFactoryBean);
}

可以看到如果 beanName 前面加上 & 获取的是 FactoryBean 本身,不加获取的 getObject() 返回的对象。

FactoryBean 的特殊之处在于它可以向容器中注册两个 bean,一个是它本身,一个是 FactoryBean.getObject() 方法返回值所代表的 bean。

bean 的加载#
AbstractBeanFactory#getBean#

Copy
public T getBean(String name, Class requiredType) throws BeansException {
// 调用doGetBean方法(方法以do开头实际做操作的方法)
return doGetBean(name, requiredType, null, false);
}

/**

  • @param name bean的名称

  • @param requiredType bean的类型

  • @param args 显示传入的构造参数

  • @param typeCheckOnly 是否仅仅做类型检查
    */
    protected T doGetBean(final String name, @Nullable final Class requiredType,
    @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    // 获取bean的实际名称,见下文详解
    final String beanName = transformedBeanName(name);
    Object bean;

    // 直接尝试从缓存获取或 singletonFactories 中的 ObjectFactory 中获取,见下文详解
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
    // 检查bean是否是FactoryBean的实现。不是直接返回bean,
    // 是的话首先检查beanName是否以&开头,如果是返回FactoryBean本身,
    // 不是调用FactoryBean#getObject()返回对象,见下文详解
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
    // 只有在单例情况下才会去尝试解决循环依赖,原型模式下,如果存在A中有
    // B属性,B中有A属性,那么当依赖注入时,就会产生当A还未创建完的时候
    // 对于B的创建而在此返回创建A,造成循环依赖
    if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
    }

      // 检查当前bean的BeanDefinition是否在当前的bean工厂中,
      // 不在递归调用父工厂的getBean()去获取bean
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
          String nameToLookup = originalBeanName(name);
          if (parentBeanFactory instanceof AbstractBeanFactory) {
              return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
          }
          else if (args != null) {
              return (T) parentBeanFactory.getBean(nameToLookup, args);
          }
          else if (requiredType != null) {
              return parentBeanFactory.getBean(nameToLookup, requiredType);
          }
          else {
              return (T) parentBeanFactory.getBean(nameToLookup);
          }
      }
      // 如果不是仅仅做类型检查,则是创建bean,这里要进行记录
      if (!typeCheckOnly) {
          // 记录bean已经创建过
          markBeanAsCreated(beanName);
      }
    
      try {
          // 合并BeanDefinition,见下文详解
          final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
          checkMergedBeanDefinition(mbd, beanName, args);
    
          // 实例化bean前先实例化依赖bean,也就是depends-on属性中配置的beanName
          String[] dependsOn = mbd.getDependsOn();
          if (dependsOn != null) {
              for (String dep : dependsOn) {
                  // 检查是否循环依赖,即当前bean依赖dep,dep依赖当前bean,见下文详解
                  if (isDependent(beanName, dep)) {
                      throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                  }
                  // 将dep和beanName的依赖关系放入到缓存中,见下文详解
                  registerDependentBean(d
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值