- FactoryBean介绍
- FactoryBean的使用
- 使用示例
- 配置
- 运行结果
- 获取FactoryBean自身工厂对象
- FactoryBean的好处
- FactoryBean与BeanFactory的区别
- 源码分析
- FactoryBean的缓存区
- 执行流程
- 源码解析
- FactoryBean的初始化过程
- 获取FactoryBean实际生产对象过程
- 总结
FactoryBean
是Spring提供的一个可以由用户自定义的Bean实例工厂接口,FactoryBean
的对象本身由Spring容器管理,但FactoryBean
仅作为对象工厂暴露,其生产出的Bean的过程由用户控制。
FactoryBean
接口提供了如下方法:
方法 | 描述 |
---|---|
T getObject() | 获取该FactoryBean 所生产的实际对象, BeanFactory 在获取FactoryBean 生产的实际对象时,会根据其isSingleton() 方法来判定是否创建的对象属于singleton 或是prototype 。 |
Class> getObjectType(); | 返回工厂生产的实际对象类型。 |
boolean isSingleton(); | 所生产的对象是否是单例;此方法告知beanFactory ,每次获取该工厂的bean对象时是每次调用getObject() 来获取新的对象,还是始终获取唯一的单例对象。 |
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));
}
}
配置
<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自身的工厂对象需要在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
的支持。
首先看下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值,并不带&符号;
那么问题来了,由于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
自身对象;
注意需要再配置
的时候加上id值,没有加id值是不能默认通过首字母小写的类名获取到FactoryBean
的
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)