Spring源码系列(五)——FactoryBean的使用与源码分析
作者:互联网
一、FactoryBean介绍
1. FactoryBean使用
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
包裹一层呢?这样很麻烦啊
2. FactoryBean作用
为什么需要使用FactoryBean
呢?
-
考虑下,如果需要实例化一个复杂
Bean
,按照传统方式,我们需要在<bean>
中提供大量的配置信息,但这些信息还是我们不需要去关注的,我们只想要创建一个Bean
,并不想知道这个Bean
是经过多么复杂的流程创建出来的,这时就可以使用FactoryBean
来把一些配置信息、依赖项进行封装在内部,很简单的来实例化一个Bean
-
和第三方框架的整合,首先在第三方框架内部是不会出现像
@Component
这样的Spring
注解,为什么呢?因为如果使用了@Component
注解,是不是就要依赖Spring
。第三方框架与Spring
框架整合,就是需要将第三方框架中的类转化为BeanDefinition
放到Spring
容器中去,以注解或者XML
形式都行。往往第三方类都有一个非常重要的类,我们只需要将这个类注入到Spring
就可以了,不过这个类想要正常工作可能需要其他依赖,所以我们在Spring
中想注入这个类,就需要先知道它依赖的其他类,可是其他类还会有依赖,这样做就太麻烦了,所以第三方框架可以写一个FactoryBean
类,提供一个简单的入口和Spring
框架进行整合,我们只需要将这个FactoryBean
类以注解或XML
的格式放到容器就可以了。(Mybatis
集成入Spring
就是这样)
3. MyBatis中FactoryBean使用
我们在整合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
就可以。
二、FactoryBean源码分析
1. 知识点说明
在分析源码之前先说几个重要的点:
在之前的源码分析中,一直在强调的几个集合含义
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
。
2. FactoryBean源码分析
// 测试方法
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;
}
3. 小结
在取FactoryBean
包裹的对象时,也就是不加&
符号的流程如下:
首先会对beanName
名字进行转换,获取真正的beanName
,然后根据beanName
从singletonObjects
中取出Bean
实例,如果拿出来为null
,则要进行创建步骤,如果不为空,接着判断这个Bean
,如果不是FactoryBean
类型的,或者名称中是以&
开头,则直接返回取出的bean
实例
为什么会直接返回呢?因为从
singletonObjects
中拿出来的Bean
实例就是FactoryBean
如果是FactoryBean
类型的,名称中不是以&
开头,再判断是单实例还是多实例的,如果是多实例的,则直接调用FactoryBean
的getObject()
方法,如果单实例的,则先从factoryBeanObjectCache
缓存中去拿,没有的话则进行创建
创建完成后,会判断这个FactoryBean
的BeanDefinition
是不是合成,如果不是,则执行BeanPostProcessor
后置处理器的postProcessAfterInitialization
进行增强,如果需要增强的话
FactoryBean
包裹的对象是天然懒加载的,如果是单例的,则会在第一个调用getBean()
的时候创建,然后放到缓存中
标签:null,实例,Spring,beanName,object,Bean,源码,FactoryBean 来源: https://blog.csdn.net/zxd1435513775/article/details/121104087