Spring 之 FactoryBean 的使用和原因
实现FactoryBean接口,在getObject方法里面可以灵活的定义需要我们自己创建的bean实例
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/*
* 这个方法返回需要交给spring管理的对象
*/
@Nullable
T getObject() throws Exception;
/*
* 实例的类型
*/
@Nullable
Class<?> getObjectType();
/*
* 对象是否单例,默认true
*/
default boolean isSingleton() {
return true;
}
}
@Component
public class FactoryBeanDemo implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new Student();
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
}
加上@Component FactoryBeanDemo才会在spring初始化的时候实例化,但是getObject需要自己手动调用getBean()才会被调用,spring初始化的时候不会被调用
@Test
public void test1() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student student = (Student)applicationContext.getBean("factoryBeanDemo");
System.out.println(student);
}
@Test
public void test2() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
FactoryBeanDemo factoryBeanDemo = (FactoryBeanDemo)applicationContext.getBean("&factoryBeanDemo");
System.out.println(factoryBeanDemo);
}
普通的没有实现FactoryBean的类调用getBean会返回实例本身,而实现了FactoryBean的需要在beanName前面加上 & 符号才能拿到它本身,不然就会调用getObject接口。
Spring初始化第一次创建bean,DefaultListableBeanFactory类里面的preInstantiateSingletons方法,
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
//xml解析时,把所有beanName都缓存到beanDefinitionNames了
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
//把父BeanDefinition里面的属性拿到子BeanDefinition中
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//如果不是抽象的,单例的,非懒加载的就实例化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判断bean是否实现了FactoryBean接口
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
//主要从这里进入,看看实例化过程
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
先判断bean是否是实现了FactoryBean接口,如果实现了就在beanName前面加上 & 符号先把factoryBeanDemo本身实例化
再看getBean实例化方法,调到doGetBean里面
首先获取beanName ,transformedBeanName这个方法会去掉name前面带有的&,所有beanName是不包含&的,之后从缓存中获取bean,当bean实例化完成后会被保存到一级缓存里面,如果缓存里面没有会走到下面去实例化
这里可以看出不管是单例还是多列,还是走缓存都会走getObjectForBeanInstance方法
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//判断name是否以&开头
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
//如果实例不是FactoryBean类型的
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
//如果代码能走下来,则说明 beanName不是以&开头,并且beanInstance是FactoryBean类型的
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
//从缓存里面拿FactoryBean类型的实例
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
这个方法传入的name,beanName两个参数,name可能会以&开头,beanName一定是没有的
当name以&开头就表示需要获取是自己本身,所有首先以&开头是FactoryBean类型的会直接返回,不是&开头,不是FactoryBean类型的也会直接返回
只有不以&开头是FactoryBean类型的会往下走
第一次进来factoryBeanObjectCache里面是没有getObject获取的实例的,这个是单独缓存FactoryBean类型实例的map
这里就会直接调用的自己写的getObject里面获取实例,获取后保存到缓存里面。
之后调用getBean方法会直接进入doGetBean里面这时候带没有带&就会有不同的返回。