在上篇文章中咱们已经对容器的第一个扩展点(BeanFactoryPostProcessor)作了一系列的介绍。其中主要介绍了Spring容器中BeanFactoryPostProcessor的执行流程。已经Spring自身利用了BeanFactoryPostProcessor完成了什么功能,对于一些细节问题可能说的不够仔细,可是在当前阶段我想要作的主要是为咱们之后学习源码打下基础。因此对于这些问题咱们暂且不去过多纠结,待到源码学习阶段咱们会进行更加细致的分析。在本篇文章中,咱们将要学习的是容器的另外一个扩展点(FactoryBean),对于FactoryBean官网上的介绍甚短,可是若是咱们对Spring的源码有必定了解,能够发现Spring在不少地方都对这种特殊的Bean作了处理。话很少说,咱们开始进入正文。面试
咱们仍是先看看官网上是怎么说的:缓存
从上面这段文字咱们能够得出如下几个信息:mybatis
上面这些概念可能刚刚说的时候你们不是很明白,下面咱们经过FactoryBean的一些应用来进一步体会这个接口的做用。ide
咱们来看下面这个Demo:源码分析
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自己。测试
咱们先看下下面这张图:ui
涉及到FactoryBean主要在3-11-6这一步中,咱们主要关注下面这段代码:this
// .....省略无关代码.......// 1.判断是否是一个FactoryBeanif (isFactoryBean(beanName)) { // 2.若是是一个FactoryBean那么在getBean时,添加前缀“&”,获取这个FactoryBean Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; // 3.作权限校验,判断是不是一个SmartFactoryBean,而且不是懒加载的 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { // 3.判断是不是一个SmartFactoryBean,而且不是懒加载的 isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { // 4.若是是一个SmartFactoryBean而且不是懒加载的,那么建立这个FactoryBean建立的Bean getBean(beanName); } }}else { // 不是一个FactoryBean,直接建立这个Bean getBean(beanName);}// ...省略无关代码.....
咱们按照顺序一步步分析,首先看第一步:spa
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));}
这里涉及到一个概念,就是SmartFactoryBean,实际上这个接口继承了FactoryBean接口,而且SmartFactoryBean是FactoryBean的惟一子接口,它扩展了FactoryBean多提供了两个方法以下:
// 是否为原型,默认不是原型default boolean isPrototype() { return false;}// 是否为懒加载,默认为懒加载default boolean isEagerInit() { return false;}
从上面的代码中能够看出,咱们当当实现一个FactoryBean接口,Spring并不会在启动时就将这个FactoryBean所建立的Bean建立出来,为了不这种状况,咱们有两种办法:
实际上咱们能够发现,当咱们仅仅实现FactoryBean时,其getObject()方法所产生的Bean,咱们能够当前是懒加载的。
在上面的代码分析完后,在3-6-11-2中也有两行FactoryBean相关的代码,以下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 1.获取bean名称 final String beanName = transformedBeanName(name); Object bean; //...省略无关代码...,这里主要根据beanName建立对应的Bean // 2.调用getObject对象建立Bean bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
protected String transformedBeanName(String name) { // 這個方法主要用來解析別名,若是是別名的話,获取真实的BeanName return canonicalName(BeanFactoryUtils.transformedBeanName(name));} // 处理FactoryBeanpublic static String transformedBeanName(String name) { Assert.notNull(name, "'name' must not be null"); // 没有带“&”,直接返回 if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { return name; } // 去除全部的“&”,防止这种写法getBean("&&&&beanName") return transformedBeanNameCache.computeIfAbsent(name, beanName -> { do { beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); return beanName; });}
咱们能够看到,在调用getObjectForBeanInstance(sharedInstance, name, beanName, null);传入了一个参数---name,也就是尚未通过transformedBeanName方法处理的bean的名称,可能会带有“&”符号,Spring经过这个参数判断这个Bean是否是一个FactoryBean,若是是的话,会调用其getObject()建立Bean。被建立的Bean不会存放于单例池中,而是放在一个名为factoryBeanObjectCache的缓存中。具体的代码由于比较复杂,在这里咱们就暂且不分析了,你们能够先留个印象,源码阶段我会作详细的分析。
除了咱们在上文中说到的实现了FactoryBean或者SmartFactoryBean接口的Bean能够称之为一个”FactoryBean“,不知道你们对BeanDefinition中的一个属性是否还有印象。BeanDefinition有属性以下(实际上这个属性存在于AbstractBeanDefinition中):
@Nullableprivate String factoryBeanName;@Nullableprivate String factoryMethodName;
对于这个属性跟咱们这篇文章中介绍的FactoryBean有什么关系呢?
首先,咱们看看什么状况下bd中会存在这个属性,主要分为如下两种状况:
第一种状况:
@Configurationpublic 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就如咱们标题所说,是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管理,“对象”要划重点,咱们一般采用的注解如@Compent或者XML配置这种相似的操做并不能将一个对象交给Spring管理,而是让Spring根据咱们的配置信息及类信息建立并管理了这个对象,造成了Spring中一个Bean。把一个对象交给Spring管理主要有两种方式
在本文中咱们完成了对FactoryBean的学习,最重要的是咱们须要明白一点,FactoryBean是Spring中特殊的一个Bean,Spring利用它提供了另外一种建立Bean的方式,FactoryBean总体的体系比较复杂,FactoryBean是如何建立一个Bean的一些细节咱们尚未涉及到,不过不要急,在源码学习阶段咱们还会接触到它,并会对其的整个流程作进一步的分析。目前容器的扩展点咱们还剩最后一个部分,即BeanPostProcessor。BeanPostProcessor贯穿了整个Bean的生命周期,学习的难度更大。但愿你们跟我一步步走下去,认认真真学习完Spring,加油!