FactoryBean深入浅出(附源码讲解)
本文章欢迎转载,但是转载请标明出处,程序锋子https://blog.csdn.net/l13591302862/article/details/111866712
平常对
FactoryBean
的认识可能不是很深,这次花了点时间去学习了FactoryBean
,通过本篇文章进行归纳和总结,希望能够帮到小伙伴们。本人是小白,能力有限,如果有哪个地方描述错误,希望有大佬帮忙指出,先在此谢过。
1 简介
我们先来谈谈什么是 FactoryBean
FactoryBean
是一个接口,实现该接口的类可以自定义创建 bean。 FactoryBean
的实现类本质上还是 bean,经历 bean 的生命周期,但是通过 FactoryBean
创建的 bean 不经历 bean 的生命周期。
public interface FactoryBean<T> {
/** 返回创建的 bean 实例 */
T getObject() throws Exception;
/** 返回 bean 实例的类型,类型事先未知则返回 null */
Class<?> getObjectType();
/** 返回 true 表示 singleton ,否则为 prototype */
default boolean isSingleton() {
return true;
}
}
ps: 本文使用的
Spring
的版本为5.1.2
那 FactoryBean
一般是用来干什么的?
FactoryBean
一般用在框架中,用于创建复杂的 bean 实例,例如 SpringAOP 中的 ProxyFactoryBean
用于创建 AOP 代理,Dubbo 中的 ReferenceBean
用于创建远程服务代理。
2 简单用法
2.1 默认用法(延迟创建 bean)
准备实体类
public class House {
private String name;
...省略getter和setter...
}
实现 FactoryBean
接口
@Component("house")
public class HouseFactoryBean implements FactoryBean<House> {
private static final String HOUSE_NAME = "CoderFengZi";
@Override
public House getObject() throws Exception {
House house = new House();
house.setName(HOUSE_NAME);
return house;
}
@Override
public Class<?> getObjectType() {
return House.class;
}
}
使用 getBean
方法获取 bean
实例
以下要注意的是,getBean("beanName")
获取的是 FactoryBean
创建出来的 bean 实例,而 getBean("&beanName")
才是获取 FactoryBean
本身。
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
// 获取FactoryBean创建的bean实例
House house = (House) context.getBean("house");
// 获取FactoryBean本身
FactoryBean<House> factoryBean = (FactoryBean<House>) context.getBean("&house");
// 运行结果:CoderFengZi
System.out.println(house.getName());
// 运行结果:class org.coder.blog.House
System.out.println(factoryBean.getObjectType());
}
}
ps:使用的
SpringBoot
版本为2.4.1
2.2 默认是延迟创建 bean 的,如果我们想进行非延迟创建的话怎么办?
- 实现
FactoryBean
的子接口SmartFactoryBean
并重写isEagerInit
方法 - 实现
InitializingBean
接口并且在afterPropertiesSet
方法中创建 bean
a. 实现 SmartFactoryBean
的方式SmartFactoryBean
是 FactoryBean
的子接口,可以重写 isEagerInit
方法,用来进行非延迟创建 bean 实例。我们先看下 SmartFactoryBean
有什么方法?
public interface SmartFactoryBean<T> extends FactoryBean<T> {
/** 返回 true 表示 prototype,否则为 singleton */
default boolean isPrototype() {
return false;
}
/** 返回 true 表示提前创建 bean,否则为延迟创建 */
default boolean isEagerInit() {
return false;
}
}
实现 SmartFactoryBean
接口
@Component("eagerHouse")
public class EagerHouseFactoryBean implements SmartFactoryBean<House> {
private static final String HOUSE_NAME = "CoderFengZi";
@Override
public House getObject() throws Exception {
System.out.println("SmartFactoryBean eager init ...");
House house = new House();
house.setName(HOUSE_NAME);
return house;
}
@Override
public Class<?> getObjectType() {
return House.class;
}
/** 设置成非延迟创建 */
public boolean isEagerInit() {
return true;
}
}
b. 实现 InitializingBean
的方式
该接口的 afterPropertiesSet
方法会在 bean 实例化后调用AbstractAutowireCapableBeanFactory#invokeInitMethods
方法时被调用,这里不是重点,不做展开,感兴趣的朋友可以观看Spring中的InitializingBean接口的使用
@Component("house")
public class EagerHouseFactoryBean2 implements FactoryBean<House>, InitializingBean {
private static final String HOUSE_NAME = "CoderFengZi";
private volatile House house;
@Override
public House getObject() throws Exception {
if (house == null) {
house = new House();
house.setName(HOUSE_NAME);
}
return house;
}
@Override
public Class<?> getObjectType() {
return House.class;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean eager init ...");
getObject();
}
}
3 源码分析
3.1 preInstantiateSingletons
DefaultListableBeanFactory#preInstantiateSingletons
以下的代码为了方便阅读做了下精简,preInstantiateSingletons
方法是在容器 refresh
方法执行尾声,用来实例化所有的非延迟加载的单例 bean,以下代码主要解释 SmartFactoryBean
为何能进行非延迟创建。
public void preInstantiateSingletons() throws BeansException {
...省略...
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 创建所有非延迟加载的单例bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判断是否是FactoryBean
if (isFactoryBean(beanName)) {
// getBean(beanName)获取的是FactoryBean创建的bean实例
// getBean("&"+beanName)获取FactoryBean本身
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
// 判断是否有代码运行权限,这里不做展开
// 感兴趣的可以看博客https://www.jianshu.com/p/dcebd60b4c25
if (System.getSecurityManager() != null
&& factory instanceof SmartFactoryBean) {
...省略...
}
else {
// 默认情况 FactoryBean 延迟创建bean
// 但如果是 SmartFactoryBean,而且设置其 eagerInit 值为 true
// 那么就进行提前创建
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
// 进行提前创建
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
...省略...
}
3.2 getBean
由于篇幅有限,我们只研究 getBean
中与 FactoryBean
有关的代码,简单看下调用栈,可以发现最终调用的是 getObject
方法。
我们从AbstractBeanFactory#getBean
方法开始调用
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
AbstractBeanFactory#doGetBean
,该方法先从三级缓存中获取 FactoryBean
的对象,关于三级缓存可以看一文告诉你Spring是如何利用“三级缓存“巧妙解决Bean的循环依赖问题的,然后利用 FactoryBean
实例进行下一步操作,如果获取不到实例,那么就会进行bean的实例化,我们现在默认 FactoryBean
实例本身已经被实例化了。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 此处会从三级缓存中获取bean实例,我们默认FactoryBean之前已经被实例化了
// 关于三级缓存可以看https://blog.csdn.net/f641385712/article/details/92801300
// 这里可以获取到FactoryBean的实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
...省略(日志记录)...
else {
...省略(日志记录)...
}
}
// 通过FactoryBean实例来创建bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
...省略...
}
AbstractBeanFactory#getObjectForBeanInstance
,这个方法用于获取 bean 实例,先从缓存获取,如果缓存没有则创建
protected Object getObjectForBeanInstance(Object beanInstance, String name,
String beanName, @Nullable RootBeanDefinition mbd) {
// 下面这段 if 代码的意思是:
// 如果不是 FactoryBean,不要使用 & 的名字前缀
// 不然会抛出异常
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// 非 FactoryBean 直接返回 bean 实例
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
// 从缓存 factoryBeanObjectCache 的 map 中获取实例
else {
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);
}
// 判断是否是Spring的系统类
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 获取bean实例
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
FactoryBeanRegistrySupport#getObjectFromFactoryBean
,这个方法是获取 FactoryBean
实例的核心方法,主要判断是进行单例创建还是非单例创建,而且创建后运行一些后置处理逻辑,这些逻辑由 FactoryBeanRegistrySupport
的子类实现。
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);
// 使用双重校验机制,再次尝试从缓存获取
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
// 如果不是Spring的内部系统类,即!synthetic
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
// 创建单例前进行锁定,放入singletonsCurrentlyInCreation缓存
beforeSingletonCreation(beanName);
try {
// 可以进行一些后置处理,例如可以进行 AOP 的包装
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
finally {
// 创建单例后解除锁定,从singletonsCurrentlyInCreation缓存删除
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
// 非单例创建
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
}
return object;
}
}
FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
,该方法最终调用了FactoryBean
的 getObject
方法,值得注意的是使用了空对象模式,空对象模式可以看菜鸟教程空对象
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory,
String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged(
(PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 调用FactoryBean的getObject方法获取对象
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
// 即时获取的object为空也不返回null,而返回NullBean
// 这里其实使用了空对象设计模式,可以类比Optional类
// 对空对象设计模式兴趣的可以看下面的地址
// 菜鸟教程https://www.runoob.com/design-pattern/null-object-pattern.html
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(...);
}
object = new NullBean();
}
return object;
}
4 总结
-
FactoryBean
的优点是可以自定义构建 bean ,使得创建bean的过程更加灵活。 -
FactoryBean
本身可以理解成是一种策略模式,我们需要生成什么样的 bean,可以通过实现接口来自定义。这就将创建对象的行为独立出来了,符合前闭后开的原则,是一种非侵入的方式,达到了解耦的目的 - 我们平常的工作一般可能用不到
FactoryBean
,但是了解有这样一种机制对我们也是一种提升。
如果有兴趣可以微信搜一搜
程序锋子
,关注本人的微信公众号
本文地址:https://blog.csdn.net/l13591302862/article/details/111866712