Spring源码分析-从源码看BeanFactory和FactoryBean的区别

导语
  在使用Spring 中最为核心的操作就是Bean的创建以及使用。下面就来带着大家一起来分析一下关于Spring的Bean的加载相关的知识

文章目录

    • BeanFactory getBean方法
      • 原理分析
        • 1、转换对应的BeanName
        • 2、尝试从缓存中加载单例
        • 3、Bean的实例化
        • 4、原型模式依赖检查
        • 5、检查ParentBeanFactory
        • 6、将XML配置文件中的对象关系进行转换
        • 7、依赖查找
        • 8、根据不同的作用域创建
        • 9、类型转换
    • FactoryBean的使用
      • 原理分析
    • 总结

  熟悉Spring的人都知道,对于容器的操作,都是从一个BeanFactory的接口而来。而在使用Bean的时候调用的就是getBean方法来获取到需要的Bean对象。如下图所示,getBean()方法在一下的一些类里面被实现了这里值得关注的有两个类AbstractBeanFactory 和StaticListableBeanFactory 两个类,这两个类直接或者间接的实现的getBean()的方法,下面就来进入到AbstractBeanFactory 的getBean方法中进行分析。

  根据框架源码的一贯习惯,真正执行操作的并不是这个getBean()的方法,而是其中的以do开头的方法。那么就进入到其中看看都有那些操作?

Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第1张图片

BeanFactory getBean方法

原理分析

  进入到AbstractBeanFactory 类中会看到根据Bean的名称获取Bean对象的操作是通过了如下的一层封装。其中确实是有一个doGetBean()的方法。下面就来看看这个doGetBean方法。

@Override
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

  在这个方法中传入了四个参数

  • name:Bean的name;
  • requiredType:需要的Bean是什么类型的,
  • args: 传入的其他参数列表
  • typeCheckOnly :是否进行类型检查
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException 
			

  对于上面的这些参数,先不做过多的介绍,在具体调用到这些参数的时候就会知道它们具体的操作是什么了。

1、转换对应的BeanName

  进入该方法之后,对于对应的BeanName进行了提取,也许有人会问,在使用的时候我们传入的不就是我们定义的Bean Name么,直接进行查找不就可以了么,为什么还要进行多余的提取操作呢?这里,对于Bane Name的提取其实是为了解决一个问题,在我们使用的时候并不是所有的Bean都进行Name的设计,在很多的场景下,我们只是对我们能操作的Bean或者是需要的地方进行的name 的配置,但是在很多的场景下都是没有以类名小写直接作为BeanName 存在。为了统一 默认的BeanName和自定义的BeanName所以进行了一个BeanName的提取操作。根据传入的BeanName来确定一个可用的BeanName。

//提取对应的beanName
final String beanName = transformedBeanName(name);
Object bean;

2、尝试从缓存中加载单例

   接下来会进行从缓存中检查对应实例的操作,在创建单实例Bean的时候回存在依赖注入的操作,如果已经存在了对应的Bean实例就会出现循环注入的错误。Spring 的原则就是在创建Bean的时候不等到Bean创建完成就将BeanFactory进行曝光,也将BeanFactory加入到对应的缓存中,从而告诉其他调用,有对应的BeanFactory来创建该对象,如果需要进行创建的话直接可以从缓存中进行获取到该BeanFactory进行创建对象。代码如下。


Object sharedInstance = getSingleton(beanName);

3、Bean的实例化

  在缓存中获取的到Bean的原始状态,需要对Bean对象进行实例化。这里需要说明的就是在操作的时候其实在缓存中放入的就是Bean的原始状态,并不是最终需要的Bean对象。从缓存中获取到之后还需要进一步的进行加工。会看到,下面代码首先对缓存中获取到的对象进行判断,最终调用了bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 方法返回了一个bean,而这个Bean就是最终需要的Bean对象。
Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第2张图片

4、原型模式依赖检查

  在上面的代码中看到了能从缓存中获取到原始的Bean对象的情况,那么在缓存中没有对应的BeanName对应的Bean对象的时候又如何进行处理呢?我们都知道,在原型模式下,如果一个对象A以对象B作为属性,而在对象B中又以对象A作为属性那么就会出现循环依赖。所以在进行操作的时候先进行了一个循环依赖的检查
在这里插入图片描述

5、检查ParentBeanFactory

  会看到在进行完循环依赖检查之后,进行了ParentBeanFactory的检查。这有点类似于双亲委托机制。但是在进入之前首先做了一个判空的操作,也就是说如果parentBeanFactory为空的话什么都是没有用的,这里还有另外的一个判断就是!containsBeanDefinition(beanName),也就是容器中已经加载的Bean中去寻找,如果也没有找到。也就用到了另外的一个概念BeanDefinition 。
Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第3张图片

6、将XML配置文件中的对象关系进行转换

  从XML配置文件中读取到的Bean的配置是存储在一个通用的GenericBeanDefinition的定义中,这里需要转换成RootBeanDefinition,同时如果父类不为空的话需要对父类属性进行合并操作。
在这里插入图片描述

7、依赖查找

  在Bean的初始化过程中可能会用到某些属性,而这些属性可能是动态配置的,并且可能已经配置成了其他的Bean依赖,这个时候就需要先加载依赖的Bean,所以在Spring的加载顺序就是在初始化某个Bean的时候先会初始化这个Bean所对应的依赖。
Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第4张图片

8、根据不同的作用域创建

  在Spring中支持多种作用域的配置,但是在Spring本身的支持中只支持了多实例和单实例两种方式,默认使用的单实例。在这个步骤中其实就是根据不同的作用域来初始化Bean。这里需要注意的是初始化Bean和初始化BeanDefinition是不一样的。
Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第5张图片

9、类型转换

  当Bean被初始化之后整个的操作就已经基本结束了,但是还有一种情况需要进行考虑,就是类型转换的问题,当一个对象属性是String的时候,但是传入的类型确实一个Integer,那么这个时候就会需要类型的转换操作。当然这里不只是基本数据类型之间的转换操作。也可以以是其他引用类型之间的转换。到最后就可以调用return方法进行类型返回Bean对象的返回了。
Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第6张图片

FactoryBean的使用

原理分析

  一般情况下,Spring 是通过反射机制利用bean的Class属性指定的实现类来实例化Bean对象,在某些情况下,实例化bean 过程比较复杂,如果按照传统的方式,则需要在 bean中提供大量的配置信息,配置方式的灵活性也受到了限制,这个时候采用编码的方式也许会得到一个很好的解决方案。Spring 为此提供了一个FactoryBean的工厂类接口,用户可以通过实现该接口实现定制化Bean的逻辑。

  FactoryBean 接口对于Spring 架构来说占有重要的地位,Spring 自身就提供了70多个FactoryBean 的实现,它们隐藏了实例化一些复杂Bean的细节,给上层应用带来便利,从Spring 3.0 开始,FactoryBean开始提供泛型。通过查看源码可以知道FactoryBean中提供了三个方法

public interface FactoryBean<T> {


	@Nullable
	T getObject() throws Exception;

	@Nullable
	Class<?> getObjectType();


	default boolean isSingleton() {
		return true;
	}

}

  • T getObject() throws Exception; 返回由FactoryBean创建的Bean实例,如果isSingleton()返回true,则该实例会被放到Spring容器中单例缓存池中。
  • default boolean isSingleton() :返回由FactoryBean创建的Bean实例的作用域是Singleton还是prototype。
  • Class getObjectType(); 返回FactoryBean 创建的Bean实例的类型;

  当配置文件中bean的Class属性配置的实现类是FactoryBean的时候,通过getBean方法返回的不是FactoryBean本身,而是通过getObjectType()方法返回的对象。也就是说相当于getObjectType()方法代理了getBean的方法。
小例子
  首先来创建一个Car类型

public class Car {
    private int maxSpeed;
    private String brand;
    private double price;

    public Car(int maxSpeed, String brand, double price) {
        this.maxSpeed = maxSpeed;
        this.brand = brand;
        this.price = price;
    }

    public Car() {
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int 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;
    }


    @Override
    public String toString() {
        return "Car{" +
                "maxSpeed=" + maxSpeed +
                ", brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

  第二步继承FactoryBean实现了CarFactoryBean 类

public class CarFactoryBean implements FactoryBean {

    private String  carInfo;

    public Object 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;
    }

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

    public boolean isSingleton() {
        return false;
    }

    public String getCarInfo() {
        return carInfo;
    }

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

  第三步,编写XML的配置文件


<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"
    >

    <bean id="car" class="com.core.charp2.demo02.bean.CarFactoryBean">
        <property name="carInfo" value="地狱猫,400,20000000">property>
    bean>

beans>

  第四步,编写测试类

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:demo2.xml");
        Car car = (Car) applicationContext.getBean("car");

        System.out.println(car.toString());
    }
}

  结果分析,这里当在测试主类中调用了getBean方法之后,返回的确实是在配置文件中配置的内容。整个的过程应该是getBean()调用doGetBean方法来进行反射加载的操作,但是在调用反射的时候发现CarFactoryBean 实现了 FactoryBean接口,所以容器本身就调用了getObject()方法进行了返回,这样就直接获取到了Car对象,因为在上面的代码中可以看到在getObject()方法中返回的就是一个Car对象。那么问题来了,在之前使用Bean配置文件的时候都是直接返回配置文件中配置的对象,那么我们怎么获取到CarFactoryBean对象呢?在分析BeanFactory的时候在源码中会看到如下的一段代码,在注释中明确的说明,如果想要获取FactoryBean对象需要在getBean()方法的时候加入&符号。也就是getBean("&car"),这样的操作就会获取到CarFactroyBean 对象了。
Spring源码分析-从源码看BeanFactory和FactoryBean的区别_第7张图片

总结

  通过上面的分析,了解了BeanFactory和FactoryBean的区别,并且对用户使用的Bean对象与BeanDefinition等都有了新的认识。BeanDefinition的创建只是一层粗加工的结果,getBean中的操作才是精细加工的效果。

你可能感兴趣的:(Spring)