FactoryBean的使用

FactoryBean

FactoryBean是一个接口,接口声明如下:

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,与普通的bean不一样,FactoryBean是一种可以产生bean的 bean。

FactoryBean的示例

package com.morris.spring.entity;

import org.springframework.beans.factory.FactoryBean;

public class UserFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        return new User("morris", 18);
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
package com.morris.spring.demo.annotation;

import com.morris.spring.entity.UserFactoryBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * FactoryBean的使用
 */
public class FactoryBeanDemo {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserFactoryBean.class);
		System.out.println(applicationContext.getBean("&userFactoryBean"));
		System.out.println(applicationContext.getBean("userFactoryBean"));
		System.out.println(applicationContext.getBean("userFactoryBean"));
	}
}

运行结果如下:

com.morris.spring.entity.UserFactoryBean@1ec78b9
User(username=morris, age=18)
User(username=morris, age=18)

从运行结果可以得出以下两个结论:

  1. UserFactoryBean会向Spring器中注入两个对象,一个名为&userFactoryBean,代表FactoryBean本身,一个名为userFactoryBean,代表FactoryBean的getObject()方法返回的对象。

  2. UserFactoryBean的getObject()方法只有在使用时才会被调用,也就是只有在使用时才会完成实例化,然后添加到Spring容器中进行管理,有点类似于Bean的懒加载。

从上面的现象很容易会认为Spring的一级缓存singletonObjects容器中会存有两个对象,一个名为&userFactoryBean的FactoryBean对象,一个名为userFactoryBean的Dog对象,真的是这样吗?

FactoryBean的用途

我们常规的Bean都是使用Class的反射获取具体实例,如果Bean的获取过程比较复杂,那么常规的xml配置需要配置大量属性值,这个时候我们就可以使用FactoryBean,实现这个接口,在其getObject()方法中初始化这个bean。

使用场景:

  • mybatis的MybatisSqlSessionFactoryBean

与BeanFactory的比较

  • BeanFactory是个bean工厂,是一个工厂类(接口), 它负责生产和管理bean的一个工厂,是ioc容器最底层的接口,是个ioc容器,是spring用来管理和装配普通bean的ioc容器(这些bean称为普通bean)。

  • FactoryBean是个bean,在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean,由spring管理后,生产的对象是由getObject()方法决定的。

源码阅读

FactoryBean的初始化

Spring启动过程中会使用BeanFactoryPostProcesser收集要被Spring管理的类,封装为一个BeanDefinition,FactoryBean类也不例外。然后会遍历所有的BeanDefinition进行实例化和初始化。

DefaultListableBeanFactory#preInstantiateSingletons

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Trigger initialization of all non-lazy singleton beans...
	// 遍历所有的BD,开始实例化
	for (String beanName : beanNames) {
		// 把父BD中的属性复制到子BD中
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		/**
		 * 不是抽象的,是单例的,不是懒加载的才能被实例化
		 */
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			// 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 {
				// 普通bean的实例化
				getBean(beanName);
			}
		}
	}
... ...

如果一个Bean实现了FactoryBean接口,跟普通Bean一样会调用getBean(),只不过会在beanName前面加上&。

AbstractBeanFactory#isFactoryBean

判断一个bean是否是FactroyBean。

org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String)

public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
	String beanName = transformedBeanName(name);
	Object beanInstance = getSingleton(beanName, false);
	if (beanInstance != null) {
		// 存在实例,就判断实例是不是FactoryBean的实例
		return (beanInstance instanceof FactoryBean);
	}
	// No singleton instance found -> check bean definition.
	if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
		// No bean definition found in this factory -> delegate to parent.
		return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
	}
	// 不存在实例,就判断BD中的class(targetType)是不是实现了FactoryBean
	return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
}

判断bean是否实现了FactoryBean接口。

protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
	Boolean result = mbd.isFactoryBean;
	if (result == null) {
		Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
		result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
		mbd.isFactoryBean = result;
	}
	return result;
}

AbstractBeanFactory#doGetBean

创建FactoryBean实例,与普通Bean的创建过程是一致的。

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

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

protected <T> T doGetBean(
		String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
		throws BeansException {

	// 如果beanName是FactoryBean,那么beanName前面一定以&开头
	// transformedBeanName会把name中的&去掉
	String beanName = transformedBeanName(name);
... ...

protected String transformedBeanName(String name) {
	return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

org.springframework.beans.factory.BeanFactoryUtils#transformedBeanName

public static String transformedBeanName(String name) {
	Assert.notNull(name, "'name' must not be null");
	if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
		return name;
	}
	return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
		do {
			// 去掉&
			beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
		}
		while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
		return beanName;
	});
}

从上面的源码中可以发现FactoryBean的初始化与普通的Bean的初始化流程基本一致,最后UserFactoryBean会缓存在Spring的一级缓存容器singletonObjects,beanName为userFactoryBean,而不是&userFactoryBean(在调用的过程中判断Bean是一个FactoryBean就会给beanName加上前缀&,但是在底层的调用过程中又将beanName中的&去除了)。

另外可以发现FactoryBean.getObject()方法并未被调用。

getObject()方法的调用

FactoryBean所生产的Bean只有在调用FactoryBean.getObject()方法时才会被创建,而FactoryBean.getObject()的调用发生在对FactoryBean.getObject()返回值类型的Bean的获取时,例如:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserFactoryBean.class);
System.out.println(applicationContext.getBean("&userFactoryBean")); // 获取FactoryBean本身
System.out.println(applicationContext.getBean("userFactoryBean")); // 触发FactoryBean.getObject()
System.out.println(applicationContext.getBean("userFactoryBean")); // 只会触发一次FactoryBean.getObject()

FactoryBean.getObject()方法的被调用过程如下:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(
		String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
		throws BeansException {

	// 如果beanName是FactoryBean,那么beanName前面一定以&开头
	// transformedBeanName会把name中的&去掉
	String beanName = transformedBeanName(name);
	Object bean;

	// Eagerly check singleton cache for manually registered singletons.
	// 查询缓存
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		if (logger.isTraceEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		// 这里会对factoryBean进行特殊处理
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
... ...

当我们调用applicationContext.getBean(“userFactoryBean”),执行到上面的方法中时,会先根据userFactoryBean拿到一级缓存中的UserFactoryBean,然后执行getObjectForBeanInstance()方法,这里会对factoryBean进行特殊处理,而普通的bean就会直接返回。

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.
	// 判断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;
	}

	// 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.
	// 普通的bean直接返回
	if (!(beanInstance instanceof FactoryBean)) {
		return beanInstance;
	}

	Object object = null;
	if (mbd != null) {
		mbd.isFactoryBean = true;
	}
	else {
		// 这里会从factoryBeanObjectCache中拿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());
		// 这里面会直接调用factorybean对象的getObject()
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}

分析上面的代码:

  1. 此时参数为name=userFactoryBean,beanName=userFactoryBean,beanInstance=UserFactoryBean
  2. 所以前面的两个if都会进入。
  3. factoryBeanObjectCache中目前也没有,只有执行过一次getObject()方法后才会被缓存到factoryBeanObjectCache。
  4. 最后会调用UserFactoryBean.getObject()方法来实例化Bean,并缓存到factoryBeanObjectCache。

总结:

  1. spring一级缓存中会存储名为userFactoryBean的UserFactoryBean
  2. factoryBeanObjectCache中会存储名为userFactoryBean的User