admin管理员组

文章数量:1531792

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;

public class Dog {

	private String name;

	public Dog(String name) {
		System.out.println("new dog");
		this.name = name;
	}

}
package com.morris.spring.entity;

import org.springframework.beans.factory.FactoryBean;

public class DogFactoryBean implements FactoryBean<Dog> {
	@Override
	public Dog getObject() throws Exception {
		return new Dog("WangWang");
	}

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

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

public class FactoryBeanDemo {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
		applicationContext.register(DogFactoryBean.class);
		applicationContext.refresh();
		System.out.println(applicationContext.getBean("&dogFactoryBean"));
		System.out.println(applicationContext.getBean("dogFactoryBean"));
	}
}

运行结果如下:

com.morris.spring.entity.DogFactoryBean@1814d18
new dog
com.morris.spring.entity.Dog@5a1b69

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

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

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

从上面的现象很容易会认为Spring的一级缓存singletonObjects容器中会存有两个对象,一个名为&dogFactoryBean的FactoryBean对象,一个名为dogFactoryBean的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进行实例化和初始化。

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前面加上&。

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的初始化流程基本一致,最后DogFactoryBean会缓存在Spring的一级缓存容器singletonObjects,beanName为dogFactoryBean,而不是&dogFactoryBean(在调用的过程中判断Bean是一个FactoryBean就会给beanName加上前缀&,但是在底层的调用过程中又将beanName中的&去除了)。

getObject()方法的调用

FactoryBean所生产的Bean只有在调用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(“dogFactoryBean”),执行到上面的方法中时,会先根据dogFactoryBean拿到一级缓存中的DogFactoryBean,然后执行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=dogFactoryBean,beanName=dogFactoryBean,beanInstance=DogFactoryBean

  2. 所以前面的两个if都会进入。

  3. factoryBeanObjectCache中目前也没有,只有执行过一次getObject()方法后才会被缓存到factoryBeanObjectCache。

  4. 最后会调用DogFactoryBean.getObject()方法来实例化Bean,并缓存到factoryBeanObjectCache。

本文标签: FactoryBeanBeanFactory