Finder webserver mfc css facebook installation knockoutjs drupal7 gtk grunt alertifyjs vue下载 vue自定义组件 h5表格模板 传智播客python bootstrap框架 mysql更新多个字段 java清空数组 eclipse显示左边目录 css鼠标悬浮样式 本地安装mysql 反函数的二阶导数 python的str python调用函数 python中pop函数 python读文件 python中集合 java变量 java获取现在时间 java获取数据类型 java创建对象 php开发实例 图解设计模式 快点蛆虫成就单刷 战地联盟辅助 backtrack3 cfqq网吧任务 梦幻手游助手 win10wifi ps扭曲变形
当前位置: 首页 > 学习教程  > 编程语言

FactoryBean深入浅出(附源码讲解)

2020/12/28 19:09:09 文章标签:

本文章欢迎转载,但是转载请标明出处,程序锋子https://blog.csdn.net/l13591302862/article/details/111866712 平常对 FactoryBean 的认识可能不是很深,这次花了点时间去学习了 FactoryBean ,通过本篇文章进行归纳和总结&#xff…

本文章欢迎转载,但是转载请标明出处,程序锋子https://blog.csdn.net/l13591302862/article/details/111866712

平常对 FactoryBean 的认识可能不是很深,这次花了点时间去学习了 FactoryBean ,通过本篇文章进行归纳和总结,希望能够帮到小伙伴们。本人是小白,能力有限,如果有哪个地方描述错误,希望有大佬帮忙指出,先在此谢过。

思维导图

1 简介

我们先来谈谈什么是 FactoryBean

FactoryBean 是一个接口,实现该接口的类可以自定义创建 beanFactoryBean 的实现类本质上还是 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 的方式

SmartFactoryBeanFactoryBean 的子接口,可以重写 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 方法。
getBean调用栈我们从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,该方法最终调用了FactoryBeangetObject 方法,值得注意的是使用了空对象模式,空对象模式可以看菜鸟教程空对象

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,但是了解有这样一种机制对我们也是一种提升。

如果有兴趣可以微信搜一搜程序锋子,关注本人的微信公众号
微信公众号


本文链接: http://www.dtmao.cc/news_show_550172.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?