欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring源码之FactoryBean解析

程序员文章站 2022-05-21 19:26:26
...


1. FactoryBean是什么

FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。BeanFactory是Spring容器中的一个基本类也是很重要的一个类,在BeanFactory中可以创建和管理Spring容器中的Bean,它对于Bean的创建有一个统一的流程。

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

public interface FactoryBean<T> {

    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

FactoryBean是一个泛型接口,里面有三个方法,这三个方法实现了工厂bean的功能,在getObject里面可以自定义实例对象,getObjectType返回我们自定义实例对象的类型,isSingleton是否单例,默认为true。

2. 如何得到FactoryBean本身实例

在容器启动过程中,会调用beanFactory.preInstantiateSingletons();这个方法进行实例化bean,我们看看在循环实例化beanDefinitionNames容器中的BeanDefinition时,Spring是怎么做的

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);
        }
    }
}

在这里会判断所要实例化的bean是否是工厂bean,如果是FactoryBean,调用getBean的时候,在参数beanName的要加上前缀&,由此我们可以得出结论:获取工厂bean本身需要在beanName的前面加上前缀&

那么在getBean里面又是怎么做的呢
String beanName = transformedBeanName(name);
这里有两个变量:beanNamename
如果是工厂bean,那么name = & + beanName,这两个变量也是作为FactoryBean接口的调用入口的重要参数来使用的bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
我们来看看这个方法

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    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;
    }
}

这个方法很简单,如果是工厂bean,则返回工厂bean本身

3. 如何得到FactoryBean中的自定义对象实例

使用FactoryBean对象个性化对象创建,因为对象是在工厂bean里面创建的,不是直接暴露给Spring的,所以需要我们手动调用才会进行对象的创建。
假设我们在FactoryBean中创建了一个Student对象,那么我们需要得到Student对象,需要借助上下文对象

Student bean = applicationContext.getBean(Student.class);

我们来看看其中的重点方法resolveNamedBean,进过断点调试,在方法上写上注释

//candidateNames里面装的是Student的工厂bean标识
String[] candidateNames = getBeanNamesForType(requiredType);
if (candidateNames.length == 1) {
	String beanName = candidateNames[0];
	//这里调用getBean返回的是Student对象
	return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
}

看到这里自然就会有两个疑问,为什么入参是Student,拿到的确是工厂bean,getBean传入的是共产bean标识,拿到的确是Student对象呢,我们分别来看看这两个方法都做了什么

3.1. 首先我们先来看看getBeanNamesForType

//循环所有的beanDefinition
for (String beanName : this.beanDefinitionNames) {
	//判断是否是工厂方法
	boolean isFactoryBean = isFactoryBean(beanName, mbd);
	if (isFactoryBean) {
		if (includeNonSingletons || isNonLazyDecorated ||
						(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
			//这个是主要方法,用来找到能够实例化Student的工厂bean
			matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
		}
		if (matchFound) {
			//匹配的beanName加入到集合中返回
			result.add(beanName);
		}
	}
}

重点来看看isTypeMatch方法

String beanName = transformedBeanName(name);
boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);
// Check manually registered singletons.
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
	if (beanInstance instanceof FactoryBean) {
		if (!isFactoryDereference) {
			//前面都是判断条件:如果是工厂bean,且不带&前缀,进入这个方法
			Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
			//判断工厂中的实例化类型和我们要得到的实例是不是保持一致
			return (type != null && typeToMatch.isAssignableFrom(type));
		}
	}
}

那么getTypeForFactoryBean这个方法又做了什么呢

protected Class<?> getTypeForFactoryBean(FactoryBean<?> factoryBean) {	
//看到的是调用了工厂bean中的getObjectType方法,这也是FactoryBean接口中的三个方法之一
	return factoryBean.getObjectType();
}

3.2. (T) getBean(beanName, requiredType.toClass(), args)

Object sharedInstance = getSingleton(beanName);
//这里在缓存中一定能拿到bean工厂实例,因为在容器启动过程中已经将工厂bean放到缓存中了
if (sharedInstance != null && args == null) {
	//该方法是FactoryBean接口的调用入口
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

这回name = beanName,没有前缀了,所以接着getObjectForBeanInstance往下运行

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    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;
    }

    // Now we have the bean instance, which may be a normal bean or a FactoryBean.
    // If it's a FactoryBean, we use it to create a bean instance, unless the
    // caller actually wants a reference to the factory.
    //如果实例不是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());
        //这里就是拿到Student对象的方法,我们重点来看这里
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

重点我们来看这个方法getObjectFromFactoryBean
这个方法又调用了object = doGetObjectFromFactoryBean(factory, beanName);
doGetObjectFromFactoryBean

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            //这个方法调用的就是工厂bean的getObject方法
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(
                    beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        object = new NullBean();
    }
    return object;
}

4. 小结

获取工厂对象本身要在工厂beanName前加上&标识,不加&标识获取的就是工厂bean中的getObject方法中返回的对象。