Spring 之 FactoryBean 的使用和原因

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方法,

 

Spring 之 FactoryBean 的使用和原因

@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本身实例化

Spring 之 FactoryBean 的使用和原因

再看getBean实例化方法,调到doGetBean里面

Spring 之 FactoryBean 的使用和原因

首先获取beanName ,transformedBeanName这个方法会去掉name前面带有的&,所有beanName是不包含&的,之后从缓存中获取bean,当bean实例化完成后会被保存到一级缓存里面,如果缓存里面没有会走到下面去实例化

Spring 之 FactoryBean 的使用和原因

这里可以看出不管是单例还是多列,还是走缓存都会走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

Spring 之 FactoryBean 的使用和原因

Spring 之 FactoryBean 的使用和原因

这里就会直接调用的自己写的getObject里面获取实例,获取后保存到缓存里面。

之后调用getBean方法会直接进入doGetBean里面这时候带没有带&就会有不同的返回。

 

上一篇:【Spring】配置initMethod 与 配置destroyMethod 的实现原理(七)


下一篇:Spring 最重要的bean源信息 : BeanDefinition接口相关解释