FactoryBean介绍
FactoryBean 是Spring提供的一个可以由用户自定义的Bean实例工厂接口,FactoryBean 的对象本身由Spring容器管理,但FactoryBean 仅作为对象工厂暴露,其生产出的Bean的过程由用户控制。
FactoryBean 接口提供了如下方法:
方法 | 描述 |
---|
T getObject() | 获取该FactoryBean 所生产的实际对象, BeanFactory 在获取FactoryBean 生产的实际对象时,会根据其isSingleton() 方法来判定是否创建的对象属于singleton 或是prototype 。 | Class<?> getObjectType(); | 返回工厂生产的实际对象类型。 | boolean isSingleton(); | 所生产的对象是否是单例;此方法告知beanFactory ,每次获取该工厂的bean对象时是每次调用getObject() 来获取新的对象,还是始终获取唯一的单例对象。 |
FactoryBean的使用
使用示例
package com.baiyang.factorybean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.Serializable;
class Book implements Serializable {
private static final long serialVersionUID = -672687543464668808L;
private String name;
private String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
public class BookFactory implements FactoryBean<Book> {
public static boolean isSingleton = true;
@Override
public Book getObject() throws Exception {
Book book = new Book();
book.setName("《非暴力沟通》");
book.setAuthor("马歇尔·卢森堡");
return book;
}
@Override
public Class<?> getObjectType() {
return Book.class;
}
@Override
public boolean isSingleton() {
return isSingleton;
}
public static void main(String[] args) {
System.out.println("-------工厂singleton模式------");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");
Book book1 = (Book) context.getBean("book");
System.out.println("book1:" + book1);
Book book2 = (Book) context.getBean("book");
System.out.println("book2:" + book2);
System.out.println("book1 == book2:" + (book1 == book2));
System.out.println("-------工厂prototype模式------");
BookFactory.isSingleton = false;
context = new ClassPathXmlApplicationContext("factorybean.xml");
book1 = (Book) context.getBean("book");
System.out.println("book1:" + book1);
book2 = (Book) context.getBean("book");
System.out.println("book2:" + book2);
System.out.println("book1 == book2:" + (book1 == book2));
}
}
配置
<?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">
<bean id="book" class="com.baiyang.factorybean.BookFactory"/>
</beans>
运行结果
-------工厂singleton模式------
book1:com.baiyang.factorybean.Book@5bcab519
book2:com.baiyang.factorybean.Book@5bcab519
book1 == book2:true
-------工厂prototype模式------
book1:com.baiyang.factorybean.Book@73846619
book2:com.baiyang.factorybean.Book@4bec1f0c
book1 == book2:false
可以从结果中看出:
- 尽管在xml中配置的id为"book"的对象是
BookFactory 类型,但通过getBean("book") 获取的类型是Book 类型,非BookFactory 类型 - 当
FactoryBean#isSingleton() 返回true 时多次获取的book对象都是同一个对象。 - 当
FactoryBean#isSingleton() 返回false 时多次获取的book对象都是不同的对象。
获取FactoryBean自身工厂对象
如果需要获取FactoryBean自身的工厂对象需要在BeanId前加上"&“符号;”&“符号可以理解为C语言中获取变量的地址;在此处可以理解为"beanName"如果是工厂bean,则获取的是对应工厂生产的Bean,而”&beanName"获取的是生产该"beanName"的工厂;
System.out.println("--------获取FactoryBean自身对象-------");
System.out.println("BookFactory:" + context.getBean("&book"));
FactoryBean的好处
FactoryBean与BeanFactory的区别
两者都是用来创建对象的,当使用BeanFactory对象的时候必须遵循完整的创建过程,这个过程是由Spring容器来管理控制的。而FactoryBean只需要调用getObject方法就可以返回对象了,整个对象的创建过程是由用户来控制的。
源码分析
FactoryBean的缓存区
FactoryBeanRegistrySupport 类实现了对FactoryBean 的支持;我们熟知的最常用的Bean工厂DefaultListableBeanFactory 继承了FactoryBeanRegistrySupport ,从而也获得了FactoryBean 的支持。
执行流程
源码解析
FactoryBean的初始化过程
首先看下BeanFactory 实例化所有BeanDefinition 的过程,来了解下FactoryBean 的初始化过程。 先定位到AbstractApplicationCountext 类的refresh() 方法的finishBeanFactoryInitialization(beanFactory); 位置如下: 可以看到当前BeanDefinitionMap 中注册了name 为Book 的BeanDefinition ,其beanClass 指向的FactoryBean 实现类BookFactory ;
refresh() 调用finishBeanFactoryInitialization(beanFactory); 的最后会调用DefaultListableBeanFactory#preInstantiateSingletons() 方法来对BeanFactory 中注册的所有BeanDefinition 进行初始化: 如上图可以看到最外层循环所有注册的BeanDefinition 的代码有区别BeanName 是否是FactoryBean 来走不同的逻辑。
看下isFactoryBean() 的方法是如何判断其为FactoryBean 的: org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String)
org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition) 然后是对FactoryBean 的实例化关键点,由于前面对FactoryBean 的name 加了"&“前缀,那么注册到BeanFactory 中的单例Bean 的key 是否也是加了”&"符号的呢? 继续看下: org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 上面的第一步transformedBeanName(name) 方法会将传入的name转成常规name,即:如果有"&“前缀则将”&"前缀去除。 然后对去除了&的name进行初始化,因为只有这样才能通过转成了常规的name找到BeanDefinition 。
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance 经过以上过程,最终会将FactoryBean 对象注册到BeanFactory 的singletonObjects 属性中,并且name为设置的id值,并不带&符号;
获取FactoryBean实际生产对象过程
那么问题来了,由于singletonObjects 里面注册的Bean 是FactoryBean 对象,并不是实际的bean,那么Spring是怎么调用的是FactoryBean 的getObject 方法返回的呢? 换句话说,Spring用FactoryBean生产出实际bean对象的过程是怎么样的呢?
我们从getBean 方法开始: 一路Debug到下面doGetBean : 由于FactoryBean 在容器初始化时就已经实例化注册到了BeanFactory 中,所以Object sharedInstance = getSingleton(beanName); 这行代码是可以找到FactoryBean 对象的。
关键在上图的bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 这行代码的调用。 由于是获取FactoryBean 的实际Bean,所以是没有BeanDefinition 的,所以传入的实参是null。 进入看下实现: 由于传入的name不是以&符号开头,所以获取的是FactoryBean生产的对象,首先是从factoryBeanObjectCache 缓存中通过beanName获取,如果获取到则直接返回,如果获取不到,则调用object = getObjectFromFactoryBean(factory, beanName, !synthetic); 这行代码获取生产的实际对象。
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean 从上图的过程可以看到factory 的isSingleton 返回的是true 的话,那么会首先从factoryBeanObjectCache 中获取,如果缓存中获取不到则调用doGetObjectFromFactoryBean 方法来实际调用FactoryBean 的getObject 来获取对象,并将对象放入到factoryBeanObjectCache 中用于后续的直接获取。 如果是非单例的话,则直接调用FactoryBean 的getObject 来每次都新创建对象返回。
返回实际生产对象: org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean 最终在BeanFactory 中呈现的内存情况: 可以看到工厂生产出来的实际对象存放在factoryBeanObjectCache 变量中,工厂对象在容器初始化时注册在singletonObjects 中; 并且两个变量中的key是相同的;
总结
Spring 的FactoryBean 的实现关键在FactoryBeanRegistrySupport 类,FactoryBeanRegistrySupport 提供了一系列对FactoryBean的支持,其内部维护了一个factoryBeanObjectCache 变量作为FactoryBean 生产的单例bean 的支持。FactoryBean 在容器初始化时就会被实例化到BeanFactory 的singletonObjects 中,FactoryBean 的getObject 生产出的bean如果是单例,则会存放到factoryBeanObjectCache 变量中,便于后续的直接获取。- 通过
getBean("name") 获取的是FactoryBean 生产的实际bean对象,通过getBean("&name") 获取的是FactoryBean 自身对象;
注意需要再配置<bean> 的时候加上id值,没有加id值是不能默认通过首字母小写的类名获取到FactoryBean 的
|