容器的扩展点(FactoryBean)

概述

  1. FactoryBean主要用来定制化Bean的创建逻辑
  2. 当我们实例化一个Bean的逻辑很复杂的时候,使用FactoryBean是很必要的,这样可以规避我们去使用冗长的XML配置
  3. FactoryBean接口提供了以下三个方法:
  • Object getObject(): 返回这个FactoryBean所创建的对象。
  • boolean isSingleton(): 返回FactoryBean所创建的对象是否为单例,默认返回true。
  • Class getObjectType(): 返回这个FactoryBean所创建的对象的类型,如果我们能确认返回对象的类型的话,我们应该正常对这个方法做出实现,而不是返回null。
  1. Spring自身大量使用了FactoryBean这个概念,至少有50个FactoryBean的实现类存在于Spring容器中
  2. 假设我们定义了一个FactoryBean,名为myFactoryBean,当我们调用getBean(“myFactoryBean”)方法时返回的并不是这个FactoryBean,而是这个FactoryBean所创建的Bean,如果我们想获取到这个FactoryBean需要在名字前面拼接"&",行如这种形式:getBean("&myFactoryBean")

FactoryBean的应用

public class MyFactoryBean implements FactoryBean {
	@Override
	public Object getObject() throws Exception {
		System.out.println("执行了一段复杂的创建Bean的逻辑");
		return new TestBean();
	}

	@Override
	public Class<?> getObjectType() {
		return TestBean.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}
}

public class TestBean {
	public TestBean(){
		System.out.println("TestBean被创建出来了");
	}
}
// 测试类
public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac=
				new AnnotationConfigApplicationContext(Config.class);
		System.out.println("直接调用getBean(\"myFactoryBean\")返回:"+ac.getBean("myFactoryBean"));
		System.out.println("调用getBean(\"&myFactoryBean\")返回:"+ac.getBean("&myFactoryBean"));
	}
}

运行后结果如下:

执行了一段复杂的创建Bean的逻辑
TestBean被创建出来了
直接调用getBean("myFactoryBean")返回:com.dmz.official.extension.factorybean.TestBean@28f67ac7 
调用getBean("&myFactoryBean")返回:com.dmz.official.extension.factorybean.MyFactoryBean@256216b3

我们虽然没有直接将TestBean放入Spring容器中,但是通过FactoryBean也完成了这一操作。同时当我们直接调用getBean(“FactoryBean的名称”)获取到的是FactoryBean创建的Bean,但是添加了“&”后可以获取到FactoryBean本身。

FactoryBean相关源码分析

finishBeanFactoryInitialization(beanFactory)–>beanFactory.preInstantiateSingletons()

public void preInstantiateSingletons() throws BeansException {
		if (logger.isDebugEnabled()) {
			logger.debug("Pre-instantiating singletons in " + this);
		}
		//所有bean的名字
		// 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...
		// 触发所有非延迟加载单例beans的初始化,主要步骤为调用getBean
		for (String beanName : beanNames) {
			//合并父BeanDefinition
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					//如果是FactoryBean则加上&
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final 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) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}
  1. 判断是不是一个FactoryBean,对应源码如下:
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);
    // 直接从单例池中获取这个Bean,然后进行判断,看是否是一个FactoryBean
    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null) {
        return (beanInstance instanceof FactoryBean);
    }
    // 查找不到这个BeanDefinition,那么从父容器中再次确认是否是一个FactoryBean
    if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
        // No bean definition found in this factory -> delegate to parent.
        return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
    }
    // 从当前容器中,根据BeanDefinition判断是否是一个FactoryBean
    return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
}
  1. 如果是一个FactoryBean那么在getBean时,添加前缀“&”,获取这个FactoryBean
  2. 判断是否是一个SmartFactoryBean,并且不是懒加载的
    这里涉及到一个概念,就是SmartFactoryBean,实际上这个接口继承了FactoryBean接口,并且SmartFactoryBean是FactoryBean的唯一子接口,它扩展了FactoryBean多提供了两个方法如下:
// 是否为原型,默认不是原型
default boolean isPrototype() {
    return false;
}

// 是否为懒加载,默认为懒加载
default boolean isEagerInit() {
    return false;
}

从上面的代码中可以看出,我们当当实现一个FactoryBean接口,Spring并不会在启动时就将这个FactoryBean所创建的Bean创建出来,为了避免这种情况,我们有两种办法:

  • 实现SmartFactoryBean,并重写isEagerInit方法,将返回值设置为true
  • 我们也可以在一个不是懒加载的Bean中注入这个FactoryBean所创建的Bean,Spring在解决依赖关系也会帮我们将这个Bean创建出来
    实际上我们可以发现,当我们仅仅实现FactoryBean时,其getObject()方法所产生的Bean,我们可以当前是懒加载的。
  1. 如果是一个SmartFactoryBean并且不是懒加载的,那么创建这个FactoryBean创建的Bean。这里需要注意的是此时创建的不是这个FactoryBean,以为在getBean时并没有加一个前缀“&”,所以获取到的是其getObject()方法所产生的Bean。

  2. 看看Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);

/**
			 * 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
			 * sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
			 * bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
			 * 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
			 */
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
Object object = null;
		if (mbd == null) {
			//查询缓存
			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());
			//获取Bean
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							beforeSingletonCreation(beanName);
							try {
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
							finally {
								afterSingletonCreation(beanName);
							}
						}
						if (containsSingleton(beanName)) {
							//查询完存入factoryBeanObjectCache缓存
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				}
				return object;
			}
		}

Spring中”FactoryBean“概念的汇总(纯粹个人观点)

除了我们在上文中说到的实现了FactoryBean或者SmartFactoryBean接口的Bean可以称之为一个”FactoryBean“,不知道大家对BeanDefinition中的一个属性是否还有印象。BeanDefinition有属性如下(实际上这个属性存在于AbstractBeanDefinition中)

@Nullable
private String factoryBeanName;
@Nullable
private String factoryMethodName;

对于这个属性跟我们这篇文章中介绍的FactoryBean有什么关系呢?

首先,我们看看什么情况下bd中会存在这个属性,主要分为以下两种情况:

第一种情况:

@Configuration
public class Config {
	@Bean
	public B b(){
		return new B();
	}
}

我们通过@Bean的方式来创建一个Bean,那么在B的BeanDefinition会记录factoryBeanName这个属性,同时还会记录是这个Bean中的哪个方法来创建B的。在上面的例子中,factoryBeanName=config,factoryMethodName=b。

第二种情况:

<bean id="factoryBean" class="com.dmz.official.extension.factorybean.C"/>

<bean id="b" class="com.dmz.official.extension.factorybean.B" factory-bean="factoryBean" factory-method="b"/>

通过XML的方式进行配置,此时B的BeanDefinition中factoryBeanName=factoryBean,factoryMethodName=b。
上面两种情况,BeanDefinition中的factoryBeanName这个属性均不会为空,但是请注意此时记录的这个名字所以对于的Bean并不是一个实现了FactoryBean接口的Bean。

综上,我们可以将Spring中的FactoryBean的概念泛化,也就是说所有生产对象的Bean我们都将其称为FactoryBean,那么可以总结画图如下:
在这里插入图片描述

跟FactoryBean相关常见的面试题

FactoryBean跟BeanFactory的区别

FactoryBean就如我们标题所说,是Spring提供的一个扩展点,适用于复杂的Bean的创建。mybatis在跟Spring做整合时就用到了这个扩展点。并且FactoryBean所创建的Bean跟普通的Bean不一样。我们可以说FactoryBean是Spring创建Bean的另外一种手段。

而BeanFactory是什么呢?BeanFactory是Spring IOC容器的顶级接口,其实现类有XMLBeanFactory,DefaultListableBeanFactory以及AnnotationConfigApplicationContext等。BeanFactory为Spring管理Bean提供了一套通用的规范。接口中提供的一些方法如下:

boolean containsBean(String beanName)

Object getBean(String)

Object getBean(String, Class)

Class getType(String name)

boolean isSingleton(String)

String[] getAliases(String name)

通过这些方法,可以方便地获取bean,对Bean进行操作和判断。

如何把一个对象交给Spring管理

首先,我们要弄明白一点,这个问题是说,怎么把一个对象交給Spring管理,“对象”要划重点,我们通常采用的注解如@Compent或者XML配置这种类似的操作并不能将一个对象交给Spring管理,而是让Spring根据我们的配置信息及类信息创建并管理了这个对象,形成了Spring中一个Bean。把一个对象交给Spring管理主要有两种方式

  • 就是用我们这篇文章中的主角,FactoryBean,我们直接在FactoryBean的getObject方法直接返回需要被管理的对象即可
  • @Bean注解,同样通过@Bean注解标注的方法直接返回需要被管理的对象即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值