FactoryBean -【Spring底层原理】

Spring框架底层原理 专栏收录该内容
17 篇文章 0 订阅

目录

 

一、FactoryBean用法

二、实例分析

三、源码追踪

四、总结


一、FactoryBean用法

注意:这里是FactoryBean,而不是BeanFactory

  • FactoryBean是一个工厂Bean,用于生成某一个类型Bean实例
  • BeanFactorySpring容器中的一个基本类也是很重要的一个类,用于创建和管理Spring容器中的Bean

FactoryBean首先它是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。源码如下:

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    // 返回一个对象实例,这个对象会添加到容器中
    @Nullable
    T getObject() throws Exception;

    // Bean的类型
    @Nullable
    Class<?> getObjectType();

    // 控制是否是单例,返回true为单例(容器中保存一份),返回false为多例(每次获取调用getObject()创建新的)
    default boolean isSingleton() {
        return true;
    }
}

FactoryBean接口中,有三个方法,使用的时候通过调用工厂Bean获取FactoryBeangetObject方法创建对象:

  • getObject():返回一个对象实例,这个对象会添加到容器中
  • getObjectType():获取Bean的类型
  • isSingleton():控制是否是单例,返回true为单例(容器中保存一份),返回false为多例(每次获取调用getObject()创建新的)

二、实例分析

FactoryBean作为一个生产或修饰对象的工厂Bean,那是如何生产Bean的呢,咱们通过实例来进行分析,这里就使用工厂Bean来生产Color对象

// 启动类
public class MainTest {
    @Test
    public void TestMain(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }

        // 工厂Bean获取的是getObject创建的对象
        Object factoryBean = applicationContext.getBean("colorFactoryBean");
        System.out.println("Bean的类型" + factoryBean.getClass());

        // 测试isSingleton控制单例多例
        Object factoryBean2 = applicationContext.getBean("colorFactoryBean");
        System.out.println("Bean的类型" + factoryBean.getClass());
        System.out.println(factoryBean == factoryBean2);

        // 通过加“&”获取ColorFactoryBean对象
        Object factoryBean3 = applicationContext.getBean("&colorFactoryBean");
        System.out.println(factoryBean3.getClass());
    }
}

// 待生产的Color对象
public class Color {
}

// 创建一个spring定义的工厂Bean
public class ColorFactoryBean implements FactoryBean {
    // 返回一个color对象,这个对象会添加到容器中
    public Object getObject() throws Exception {
        return new Color();
    }

    // Bean的类型
    public Class<?> getObjectType() {
        return Color.class;
    }

    // 控制是否是单例,返回true为单例(容器中保存一份),返回false为多例(每次获取调用getObject()创建新的)
    public boolean isSingleton() {
        return false;
    }
}

// 配置类
@Configuration
public class AppConfig {
    /**
     * 1.默认获取的是工厂Bean调用getObject创建的对象
     * 2.要获取工厂Bean本身,需要给ID前面加一个“&”
     */
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
}

运行启动类,可以看到,已经将Bean对象给生产出来了,根据打印信息,可以得出以下结论:

  • 工厂Bean获取的是getObject所创建的对象,这也就是所谓的生产Bean
  • 通过改变工厂类的isSingleton方法返回值可以改变创建Bean的单例还是多例
  • 如果要将FactoryBean本身注入进spring容器中,获取的时候需要给ID前面加一个“&”

image-20210301164656868

三、源码追踪

参考:https://www.cnblogs.com/guitu18/p/11284894.html

FactoryBean是怎么让Spring容器管理调用它的getObject所生成的Bean的,咱们通过源码来看看FactorBean是如何生产Bean的,

在启动类中通过调用:AnnotationConfigApplicationContext——> refresh() 方法——> getBean()方法,再到AbstractBeanFactory实现类,在这个类中,又调用了doGetBean方法,doGetBean可以说是Spring容器中一个很核心的一个类,里面的功能很多很复杂,我们在这篇文章中只关注和FactoryBean相关的内容。截取部分代码:

【1】doGetBean方法中调用getSingleton方法 从Spring容器中获取单例Bean

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = this.transformedBeanName(name);
    // 调用getSingleton方法 从Spring容器中获取单例Bean
    Object sharedInstance = this.getSingleton(beanName);
    Object bean;
    if (sharedInstance != null && args == null) {
        if (this.logger.isTraceEnabled()) {
            if (this.isSingletonCurrentlyInCreation(beanName)) {
                this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
            } else {
                this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }

        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
    }
    ....
}

getSingleton从Spring容器中获取单例Bean

@Nullable
public Object getSingleton(String beanName) {
    return this.getSingleton(beanName, true);
}

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先从singletonObjects中获取单例Bean singletonObjects是一个ConcurrentHashMap
    // key是beanName value是单例Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果没有获取到,则判断是不是当前在创建中的单例Bean
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized(this.singletonObjects) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }

    return singletonObject;
}

【2】doGetBean方法中调用getObjectForBeanInstance,关键代码就从这里开始,查看源码:

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    // 这里判断 name是不是以&开头,不是经过处理的beanName 并且这个bean实例 不是FactoryBean类型的
    // 如果是&开头并且不是FactoryBean类型 则抛出异常
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
            // 不是FactoryBean类型 或者name以&开头 直接返回bean实例,要根据beanName获取真正的FactoryBean实例的时候,在beanName前面加上&
        } else if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        } else {
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }

            return beanInstance;
        }
    } else if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    } else {
        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        } else {
            // factoryBeanObjectCache 看看是不是在缓存中存在
            object = this.getCachedObjectForFactoryBean(beanName);
        }

        // 如果没有
        if (object == null) {
            // 如果能走到这里来 这个bean实例是FactoryBean类型的
            FactoryBean<?> factory = (FactoryBean)beanInstance;
            if (mbd == null && this.containsBeanDefinition(beanName)) {
                mbd = this.getMergedLocalBeanDefinition(beanName);
            }

            boolean synthetic = mbd != null && mbd.isSynthetic();
            //从这个方法的名字我们可以看到这个方法的意思是:从FactoryBean中获取对象
            object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);
        }

        return object;
    }
}

分析如下:

  1. 第一个判断BeanFactoryUtils.isFactoryDereference:判断name是否不为空且以&开头,如果是&开头并且不是FactoryBean类型 则抛出异常
  2. 后面的判断beanInstance instanceof FactoryBean:不是FactoryBean类型 或者name以&开头 直接返回bean实例,要根据beanName获取真正的FactoryBean实例的时候,在beanName前面加上&
public static boolean isFactoryDereference(@Nullable String name) {
    return name != null && name.startsWith("&");
}

如果beanInstance不属于FactoryBean或其子类的实例,或者name是以&开头就直接返回实例对象beanInstance,否则进入到if分支中。在if分支里的各种if .. ==null判断是为了提高性能,咱们只挑关键部分看:object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);继续跟踪进去看代码。

【3】getObjectFromFactoryBean调用doGetObjectFromFactoryBean方法

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // FactoryBean类型的实例 调用isSingleton方法返回的是true,所传入的bean实例也要求是单例类型的
    if (factory.isSingleton() && this.containsSingleton(beanName)) {
        synchronized(this.getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 调用doGetObjectFromFactoryBean方法从FactoryBean中获取bean对象,这里是调用的FactoryBean的getObject方法来获取的
                object = this.doGetObjectFromFactoryBean(factory, beanName);
                // 再从缓存中获取一次
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                // 如果上一步的缓存中获取到了则用缓存中的替代我们从FactoryBean中获取的bean
                if (alreadyThere != null) {
                    object = alreadyThere;
                } else {
                    if (shouldPostProcess) {
                        if (this.isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }

                        this.beforeSingletonCreation(beanName);

                        try {
                            object = this.postProcessObjectFromFactoryBean(object, beanName);
                        } catch (Throwable var14) {
                            throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", var14);
                        } finally {
                            this.afterSingletonCreation(beanName);
                        }
                    }

                    if (this.containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }

            return object;
        }
    } else {
        Object object = this.doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = this.postProcessObjectFromFactoryBean(object, beanName);
            } catch (Throwable var17) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", var17);
            }
        }

        return object;
    }
}

分析:

  • factory.isSingleton() && this.containsSingleton(beanName)判断就是调用isSingleton方法返回的是true,也就是对用实例中通过isSingleton方法返回true来控制单例
  • this.doGetObjectFromFactoryBean(factory, beanName):调用doGetObjectFromFactoryBean方法从FactoryBean中获取bean对象,这里是调用的FactoryBean的getObject方法来获取的

【4】调用doGetObjectFromFactoryBean方法

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = this.getAccessControlContext();

            try {
                object = AccessController.doPrivileged(factory::getObject, acc);
            } catch (PrivilegedActionException var6) {
                throw var6.getException();
            }
        } else {
            object = factory.getObject();
        }
    } catch (FactoryBeanNotInitializedException var7) {
        throw new BeanCurrentlyInCreationException(beanName, var7.toString());
    } catch (Throwable var8) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", var8);
    }

    if (object == null) {
        if (this.isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");
        }

        object = new NullBean();
    }

    return object;
}

分析:

  • object = factory.getObject():这个factory就是我们传入的beanInstance实例。绕了这么一大圈,getBean方法返回的居然是我们实现FactoryBean接口定义的getObject方法

到这里,就证实了案例中的结论了:FactoryBean是一个能生产或修饰对象生成的工厂Bean。一个Bean如果实现了FactoryBean接口,那么根据该Bean的名称获取到的实际上是getObject()返回的对象,而不是这个Bean自身实例,如果要获取这个Bean自身实例,那么需要在名称前面加上'&'符号

四、总结

  1. 循环Spring容器中所有的beanNames,再根据beanName获取对应的Bean实例
  2. 判断获取的Bean实例是不是FactoryBean类型的Bean,如果是,则调用Bean的getObjectType方法获取Class,将获取到的Class和传入的Class进行匹配,如果匹配到,则将此beanName和传入的Class建立一个映射关系
  3. 再根据beanName获取到Spring容器中对应的Bean,调用Bean的getObject方法来获取对应的实例。
  4. 如果要获取这个Bean自身实例,那么需要在名称前面加上'&'符号
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值