FactoryBean
被称为工厂Bean
。在Spring
中是个接口,用它可以生成某一个类型Bean
的实例,其最大的一个作用是:可以让我们自定义Bean
的创建过程。
在使用Spring
时,我们通过在xml
中配置<bean>
标签或者在类上添加注解@Component
来实例化Bean
,这种方式下Spring
是通过反射机制利用class
属性来实例化Bean
。我们就把这种使用方式称为传统方式吧。
但在某些情况下,如果实例化Bean
过程比较复杂,再按照上面传统的方式去创建Bean
,则需要在<bean>
标签中或者注解@Component
上提供大量的配置信息。此时传统配置的Bean
方式就很繁琐,也有局限性。那除了上面的传统使用Bean
的方式外,还有其他办法吗?
当然有了,我们还可以使用编码的方式创建Bean
。
Spring
为此提供了一个org.springframework.bean.factory.FactoryBean
接口,程序员可以通过实现该接口采用编码的方式自定义实例化Bean
的逻辑。
FactoryBean
接口在Spring
框架具有极其重要的地位,Spring
自身就提供了70多个FactoryBean
的实现,这些FactoryBean
隐藏了实例化一些复杂Bean
的细节,给上层应用带来了极大便利,下面看FactoryBean
源码:
// FactoryBean接口 public interface FactoryBean<T> { //返回对象实例 T getObject() throws Exception; //创建bean的Class类型 Class<?> getObjectType(); //true是单例,false是非单例,可以看出默认为单例的 default boolean isSingleton() { return true; } }
FactoryBean
是一个泛型类且只有三个方法,如果你想要的类是一个单例,那么只需要实现两个方法就可以了,getObject()
方法返回一个对象的实例,getObjectType()
方法返回对象的Class类型。
看下面一个例子:
// 配置类 @Configuration @ComponentScan("com.scorpios") public class AppConfig { } // FactoryBean工厂 @Component public class MasterFactoryBean implements FactoryBean { @Override public Object getObject() throws Exception { // 创建一个类 return new Master(); } @Override public Class<?> getObjectType() { // 返回类的类型 return Master.class; } @Override public boolean isSingleton() { // 单例 return true; } } // 测试方法 public static void main( String[] args ) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(AppConfig.class); annotationConfigApplicationContext.refresh(); Master master = (Master) annotationConfigApplicationContext.getBean("masterFactoryBean"); MasterFactoryBean masterFactoryBean = (MasterFactoryBean) annotationConfigApplicationContext.getBean("&masterFactoryBean"); System.out.println("-----------------------------"); System.out.println(master); System.out.println(masterFactoryBean); }
打印结果:
MasterFactoryBean
类用@Component
注解修饰,如果没有给组件添加别名,那么默认是类名第一个字母小写,即masterFactoryBean
。当我们去容器中取masterFactoryBean
的时候,就拿到Master
类型。
在获取FactoryBean
的时候,其实返回的是方法getObject()
的值,如果想要获取MasterFactoryBean
类本身,需要加上在前面加上&
符号才能获取MasterFactoryBean
工厂Bean
本身。
对于FactoryBean
是否有了基本的认识,但是不是会觉得这样做毫无意思啊,为什么要使用FactoryBean
包裹一层呢?这样很麻烦啊
为什么需要使用FactoryBean
呢?
考虑下,如果需要实例化一个复杂Bean
,按照传统方式,我们需要在<bean>
中提供大量的配置信息,但这些信息还是我们不需要去关注的,我们只想要创建一个Bean
,并不想知道这个Bean
是经过多么复杂的流程创建出来的,这时就可以使用FactoryBean
来把一些配置信息、依赖项进行封装在内部,很简单的来实例化一个Bean
和第三方框架的整合,首先在第三方框架内部是不会出现像@Component
这样的Spring
注解,为什么呢?因为如果使用了@Component
注解,是不是就要依赖Spring
。第三方框架与Spring
框架整合,就是需要将第三方框架中的类转化为BeanDefinition
放到Spring
容器中去,以注解或者XML
形式都行。往往第三方类都有一个非常重要的类,我们只需要将这个类注入到Spring
就可以了,不过这个类想要正常工作可能需要其他依赖,所以我们在Spring
中想注入这个类,就需要先知道它依赖的其他类,可是其他类还会有依赖,这样做就太麻烦了,所以第三方框架可以写一个FactoryBean
类,提供一个简单的入口和Spring
框架进行整合,我们只需要将这个FactoryBean
类以注解或XML
的格式放到容器就可以了。(Mybatis
集成入Spring
就是这样)
我们在整合MyBatis
与Spring
时需要在XML中配置SqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" /> </bean>
在SpringBoot
项目中整合MyBatis
,我们不需要进行过多的额外配置,只需要引入MyBatis
包就可以直接使用,我们并没有配置SqlSessionFactory
,但是我们没配并不代表没有,而是SpringBoot
帮我们写了,在MybatisAutoConfiguration
自动配置类中有如下一个@Bean
注解:
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // 此处就是用FactoryBean // 使用SqlSessionFactoryBean来创建的SqlSessionFactory SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } // 从this.properties中可以拿到在配置文件中 Mybatis 配置的信息 Configuration configuration = this.properties.getConfiguration(); if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new org.apache.ibatis.session.Configuration(); } // …… return factory.getObject(); }
说明:上面方法中,从this.properties属性中可以拿到在application.yaml或者是application.properties中Mybatis 配置的信息,配置如下
mybatis: mapper-locations: classpath:/mapper/**/*.xml
在配置文件中,我们可以配置关于MyBatis
的相关信息,但是不用每一个依赖项都配置,只需要配置你感兴趣的,然后关于其他的配置都会在factory.getObject()
中由MyBatis
自行去配置。
来看一下SqlSessionFactoryBean
源码
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { // 要为各种属性赋值,省略set方法 private Resource configLocation; private Configuration configuration; private Resource[] mapperLocations; private DataSource dataSource; private TransactionFactory transactionFactory; private Properties configurationProperties; private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); private SqlSessionFactory sqlSessionFactory; private String environment = SqlSessionFactoryBean.class.getSimpleName(); private boolean failFast; private Interceptor[] plugins; private TypeHandler<?>[] typeHandlers; private String typeHandlersPackage; private Class<?>[] typeAliases; private String typeAliasesPackage; private Class<?> typeAliasesSuperType; private DatabaseIdProvider databaseIdProvider; private Class<? extends VFS> vfs; private Cache cache; private ObjectFactory objectFactory; private ObjectWrapperFactory objectWrapperFactory; // 省略很多其他代码 public void afterPropertiesSet() throws Exception { this.sqlSessionFactory = this.buildSqlSessionFactory(); } protected SqlSessionFactory buildSqlSessionFactory() throws IOException { XMLConfigBuilder xmlConfigBuilder = null; Configuration configuration; if (this.configuration != null) { configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } } public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { this.afterPropertiesSet(); } // 返回的是这个SqlSessionFactory对象 return this.sqlSessionFactory; } public Class<? extends SqlSessionFactory> getObjectType() { return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); } public boolean isSingleton() { return true; } }
这个SqlSessionFactoryBean
配置是不是太麻烦了,而现在呢,我们只需要配置我们感兴趣的,其他的交给MyBatis
自己写的FactoryBean
就可以。
在分析源码之前先说几个重要的点:
在之前的源码分析中,一直在强调的几个集合含义
beanDefinitionMap:存放Spring容器中的所有的BeanDefinition
beanDefinitionNames:存放Spring容器中的所有的beanName
singletonObjects:存放Spring容器中已经完成实例化的对象,这个集合里面存在的变量表示已经完成了实例化
singletonFactories:存放 bean工厂对象解决循环依赖
earlySingletonObjects:存放原始的bean对象用于解决循环依赖,注意:存到里面的对象还没有被填充属性
FactoryBean
内部包裹的类是存在FactoryBeanRegistrySupport
类中的factoryBeanObjectCache
属性中,该属性是个Map
,FactoryBeanRegistrySupport
是DefaultSingletonBeanRegistry
的子类。
从上面的断点图可以看到,在没有执行Master master = (Master) ac.getBean("masterFactoryBean")
之前,factoryBeanObjectCache
中是没有masterFactoryBean
数据的。
下面看下这行代码执行之后的断点图
当放过上面第17行断点时,发现factoryBeanObjectCache
有了一条数据,masterFactoryBean
对应的class
类型是Master
。
通过上面断点调试得知,当我们向Spring
中注入FactoryBean
类型的Bean
,相当于注入了两个Bean
,一个在singletonObjects
中,另一个在factoryBeanObjectCache
中,但factoryBeanObjectCache
中也受Spring
的管理,并且被FactoryBean
包裹的Bean
具有天然的懒加载功能。
在singletonObjects
中的masterFactoryBean
类型为MasterFactoryBean
的Bean
,和普通的Bean
注入容器的方式一样,也可以从上图得知,在Spring
进行扫描并注入到Spring
容器的时候不会去考虑是不是FactoryBean
类型的Bean
。
// 测试方法 public static void main( String[] args ){ AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(AppConfig.class); ac.refresh(); Master master = (Master) ac.getBean("masterFactoryBean"); MasterFactoryBean masterFactoryBean = (MasterFactoryBean) ac.getBean("&masterFactoryBean"); System.out.println("-----------------------------"); System.out.println(master); System.out.println(masterFactoryBean); }
下面来分析一下从Spring
容器中拿FactoryBean
时,为什么返回的是FactoryBean
所包裹的对象,用上面测试方法作为源码分析的入口。
入口为ac.getBean("masterFactoryBean")
方法,然后会调用到AbstractApplicationContext#getBean()
方法:
@Override public Object getBean(String name) throws BeansException { // 判断容器是否处于激活状态 assertBeanFactoryActive(); // 首先获得容器,然后调用容器的getBean方法 return getBeanFactory().getBean(name); }
断点跟踪会进入到AbstractBeanFactory
类中的getBean()
方法
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){ /** * 下面这个方法是通过 name 去获取 beanName,这里为什么不使用 name 直接作为 beanName呢? * 有两个原因: * 1、name 的值可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean 实现类所创建的 bean。 * 在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储方式是一致的,即 <beanName, bean>,而 beanName 中是没有 & 这个字符的。 * 所以需要将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。(此知识点涉及接口FactoryBean的使用,可自行查阅) * 2、还是别名的问题,转换需要 &beanName */ String beanName = transformedBeanName(name); Object bean; /** * 下面这个getSingleton()方法在Bean初始化的时候会调用,在getBean()的时候也会调用, * 为什么需要这么做呢? * 也就是说Spring容器在Bean初始化的时候先获取这个Bean对象,判断这个对象是否被实例化好了, * 即是不是已经被创建了。普通情况下绝对为空,但有一种情况可能不为空 * 从Spring容器中获取一个Bean,由于Spring中Bean容器是用一个map(singletonObjects)来存储的 * 所以你可以理解getSingleton(beanName)等于beanMap.get(beanName) * 由于getBean()方法会在Spring环境初始化的时候(就是对象被创建的时候调用一次)调用一次 * 还会在Bean初始化的时候再调用一次 */ Object sharedInstance = getSingleton(beanName); // 此处拿到sharedInstance,不为空,表示已经实例化了,但属性有没有填充不确定 if (sharedInstance != null && args == null) { /** * 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果 * sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的 * bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回 * 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。 */ bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // sharedInstance为null,才会进入到这里,表示从singletonObjects中拿不到 // 省略从单例池中获取不到时,开始创建bean的过程 } return (T) bean; }
下面看下这行代码getSingleton(beanName)
,看下是如何拿从缓存中拿取Bean
的,会执行到DefaultSingletonBeanRegistry
类中的getSingleton
()方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从singletonObjects中获取bean,如果不为空直接返回,不再进行初始化工作 Object singletonObject = this.singletonObjects.get(beanName); // isSingletonCurrentlyInCreation()方法判断当前beanName是不是正在创建中 // 如果为null并且正在创建中,则会进入 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 去earlySingletonObjects中拿,注意这行集合里面存放的对象还没有被填充属性 singletonObject = this.earlySingletonObjects.get(beanName); // 如果也没拿到,说明还没有创建完成 if (singletonObject == null && allowEarlyReference) { // 去存放Bean工厂的集合里拿,看看是不是Bean工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
在获取到sharedInstance
不为空的时候,会进入下面这行代码bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)
进行进一步判断
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { // name不为空并且name是以"&"开始,进if if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } // beanInstance不是FactoryBean,则抛异常 if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } } // 如果beanInstance不是FactoryBean(也就是普通bean),则直接返回beanInstance // 如果beanInstance是FactoryBean,并且name以“&”为前缀,则直接返回beanInstance(以“&”为前缀代表想获取的是FactoryBean本身) if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } // 走到这边,代表beanInstance是FactoryBean,但name不带有“&”前缀,表示想要获取的是FactoryBean创建的对象实例 Object object = null; if (mbd == null) { // 如果mbd为空,则尝试从factoryBeanObjectCache缓存中获取该FactoryBean创建的对象实例 object = getCachedObjectForFactoryBean(beanName); } if (object == null) { // 只有beanInstance是FactoryBean才能走到这边,因此直接强转 FactoryBean<?> factory = (FactoryBean<?>) beanInstance; if (mbd == null && containsBeanDefinition(beanName)) { // mbd为空,但是该bean的BeanDefinition在缓存中存在,则获取该bean的MergedBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); } // mbd是否是合成的(这个字段比较复杂,mbd正常情况都不是合成的,也就是false boolean synthetic = (mbd != null && mbd.isSynthetic()); // 从FactoryBean获取对象实例,这句是关键代码 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } // 返回对象实例 return object; } @Nullable protected Object getCachedObjectForFactoryBean(String beanName) { // 可以看到就是从factoryBeanObjectCache里面取已经缓存的,这样第二次再去获取的时候不用再次创建 return this.factoryBeanObjectCache.get(beanName); }
下面看一下FactoryBeanRegistrySupport
类中的getObjectFromFactoryBean()
方法:
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { // 1.如果是单例,并且已经存在于单例对象缓存中,什么意思? // 就是说FactoryBean你可以在类上标注为多实例的bean,那么在多次获取FactoryBean的时候 // 实际就是多次调用FactoryBean的getObject方法,如果是单实例的,则进行缓存起来,只创建一次 if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { // 3.从FactoryBean创建的单例对象的缓存中获取该bean实例 //关于这个factoryBeanObjectCache上面我们已经介绍过了 Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // 4.调用FactoryBean的getObject方法获取对象实例 object = doGetObjectFromFactoryBean(factory, beanName); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); // 5.如果该beanName已经在缓存中存在,则将object替换成缓存中的 if (alreadyThere != null) { object = alreadyThere; } else { //6.判断需要做后置处理,shouldPostProcess传过来的是!synthetic,即为true,不是合成的 if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { return object; } beforeSingletonCreation(beanName); try { // 7.对bean实例进行后置处理,执行所有已注册的BeanPostProcessor的postProcessAfterInitialization方法 //此方法会进行AOP代理增强,所以我们在写AOP增强的时候,不只是会增强FactoryBean本身,还会增强FactoryBean所包裹的对象 object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { // 抛异常略 } finally { afterSingletonCreation(beanName); } } if (containsSingleton(beanName)) { // 8.将beanName和object放到factoryBeanObjectCache缓存中 this.factoryBeanObjectCache.put(beanName, object); } } } // 9.返回object对象实例 return object; } } else { // 10.调用FactoryBean的getObject方法获取对象实例 //可以看出来,如果是多实例的,那么每次是直接创建的 Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { // 11.对bean实例进行后置处理,执行所有已注册的BeanPostProcessor的postProcessAfterInitialization方法 object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { // 抛异常略 } } // 12.返回object对象实例 return object; } }
上述代码中的4会调用FactoryBean
的getObject()
方法获取对象实例
上述代码中的7会对Bean
实例进行后置处理
下面看下4中的doGetObjectFromFactoryBean()
方法,位于FactoryBeanRegistrySupport
类中,代码如下:
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) { Object object; try { // 1.调用FactoryBean的getObject方法获取bean对象实例 if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { // 1.1 带有权限验证的 object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 1.2 不带权限,factory.getObject就会最终执行我们实现了FactoryBean的方法 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { // 抛异常略 } // 2.getObject返回的是空值,并且该FactoryBean正在初始化中,则直接抛异常,不接受一个尚未完全初始化的FactoryBean的getObject返回的空值 if (object == null) { if (isSingletonCurrentlyInCreation(beanName)) { // 抛异常略 } object = new NullBean(); } // 3.返回创建好的bean对象实例 return object; }
上面的方法很简单,就是直接调用 FactoryBean
的 getObject()
方法来获取到对象实例。
上面代码中的7调用 FactoryBeanRegistrySupport
类中的postProcessObjectFromFactoryBean()
方法,此步骤主要是对Bean
进行后置后置增强的。
@Override protected Object postProcessObjectFromFactoryBean(Object object, String beanName) { return applyBeanPostProcessorsAfterInitialization(object, beanName); } @Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; // 1.遍历所有注册的BeanPostProcessor实现类,调用postProcessAfterInitialization方法 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { // 2.在bean初始化后,调用postProcessAfterInitialization方法 result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { // 3.如果返回null,则不会调用后续的BeanPostProcessors return result; } } return result; }
在取FactoryBean
包裹的对象时,也就是不加&
符号的流程如下:
首先会对beanName
名字进行转换,获取真正的beanName
,然后根据beanName
从singletonObjects
中取出Bean
实例,如果拿出来为null
,则要进行创建步骤,如果不为空,接着判断这个Bean
,如果不是FactoryBean
类型的,或者名称中是以&
开头,则直接返回取出的bean
实例
为什么会直接返回呢?因为从
singletonObjects
中拿出来的Bean
实例就是FactoryBean
如果是FactoryBean
类型的,名称中不是以&
开头,再判断是单实例还是多实例的,如果是多实例的,则直接调用FactoryBean
的getObject()
方法,如果单实例的,则先从factoryBeanObjectCache
缓存中去拿,没有的话则进行创建
创建完成后,会判断这个FactoryBean
的BeanDefinition
是不是合成,如果不是,则执行BeanPostProcessor
后置处理器的postProcessAfterInitialization
进行增强,如果需要增强的话
FactoryBean
包裹的对象是天然懒加载的,如果是单例的,则会在第一个调用getBean()
的时候创建,然后放到缓存中