栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring源码系列(五)——FactoryBean的使用与源码分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring源码系列(五)——FactoryBean的使用与源码分析

一、FactoryBean介绍 1. FactoryBean使用

FactoryBean被称为工厂Bean。在Spring中是个接口,用它可以生成某一个类型Bean的实例,其最大的一个作用是:可以让我们自定义Bean的创建过程

在使用Spring时,我们通过在xml中配置标签或者在类上添加注解@Component来实例化Bean,这种方式下Spring是通过反射机制利用class属性来实例化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 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是经过多么复杂的流程创建出来的,这时就可以使用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


  
  

在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/**
    String beanName = transformedBeanName(name);

    Object bean;

   
    Object sharedInstance = getSingleton(beanName);

    // 此处拿到sharedInstance,不为空,表示已经实例化了,但属性有没有填充不确定
    if (sharedInstance != null && args == null) {

       
        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) 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()的时候创建,然后放到缓存中

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号