美文网首页springboot
Spring扩展点-FactoryBean

Spring扩展点-FactoryBean

作者: Wannay | 来源:发表于2021-12-07 18:58 被阅读0次

    1.了解Spring中对于FactoryBean接口设计和使用

    首先我们来看FactoryBean的源码:

    public interface FactoryBean<T> {
        T getObject() throws Exception;
    
        Class<?> getObjectType();
    
        default boolean isSingleton() {
            return true;
        }
    }
    

    然后看它的一个子接口SmartFactoryBean

    public interface SmartFactoryBean<T> extends FactoryBean<T> {
        default boolean isPrototype() {
            return false;
        }
    
        default boolean isEagerInit() {
            return false;
        }
    }
    

    FactoryBean的作用是什么?从FactoryBean这个类的名字我们可以知道其实是一个FactoryBean,也就是一个工厂Bean,既然是工厂Bean,肯定是用来造Bean的对吧!不错FactoryBean的作用就是给容器中导入组件。

    对它的方法进行简单的介绍(既然SmartFactoryBeanFactoryBean的扩展,下面也一起讲解):

    • 1.getObject方法就是用来导入组件的,你可以在这里new一个对象并进行相关的配置,然后return就行了。
    • 2.getObjectType方法很明显嘛,就是你要导入的组件的类型,泛型T也就是你要导入的组件的类型。
    • 3.isSingleton方法用来决定这个Bean是否是单例的,isPrototype同理。
    • 4.isEagerInit方法决定即将导入这个Bean是否是懒加载的,如果return true,那么在Spring IOC容器启动的过程中就会将getObject导入的Bean进行实例化和初始化,如果return false,只要当你显式进行getBean时才会对导入的Bean进行实例化和初始化。

    下面来看一个简单的FactoryBean的使用

    @Component
    public class MyFactoryBean implements SmartFactoryBean<User> {
        @Override
        public User getObject() throws Exception {
            return new User();
        }
    
        @Override
        public Class<?> getObjectType() {
            return User.class;
        }
    
        @Override
        public boolean isEagerInit() {
            return true;
        }
    }
    

    我们通过上面的简单代码就可以为容器中导入一个FactoryBean对象,而在FactoryBean中我们又给导入了一个User对象,因为我们配置了isEagerInitreturn true,因此在IOC容器启动的过程中就把user对象完成了实例化和初始化工作。为了方便进行说明,我们把myFactoryBean这个对象称作FactoryBean,把user这个对象成为FactoryBeanObject

    2. 了解FactoryBeanSpring源码当中的实现?

    2.1 在源码当中去找到在哪去创建FactoryBeanFactoryBeanObject

    首先我们知道Spring IOC容器启动的核心流程都在AbstractApplicationContextrefresh方法中被定义了,因此我们无论看Spring的哪一块代码,都可以从refresh方法作为入口点。

    image.png

    首先我们找到实例化所有的单实例Bean的方法finishBeanFactoryInitialization,这个方法的最后一步是beanFactory.preInstantiateSingletons();,我们主要关注的就是这里的代码,这个方法的实现在它的子类DefaultListableBeanFactory当中。

    image.png

    2.2 看看FactoryBean是如何导入FactoryBeanObject的?

    我们从上面的图当中可以很清楚的看到,这里FactoryBean和非FactoryBean就走到了不同的逻辑。

    • 1.如果是非FactoryBean,直接就getBean去创建Bean了。
    • 2.如果是FactoryBean,那么就把beanName加上前缀FACTORY_BEAN_PREFIX(也就是&),然后调用getBean从容器中获取/创建对象,比如上面我导入的myFactoryBean组件,这里getBean就会使用&getFactoryBean作为beanName

    但是我们容器中真的有&getFactoryBean这个beanNameBean吗?很显然没有,我们来看getBean方法的逻辑。真正的逻辑在AbstractBeanFactory.doGetBean当中。

    2.2.1 通过getBean获取FactoryBean

    image.png

    首先,进来第一步是transformedBeanName,它做了什么呢?

        public static String transformedBeanName(String name) {
            Assert.notNull(name, "'name' must not be null");
            if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
                return name;
            }
            return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
                do {
                    beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
                }
                while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
                return beanName;
            });
        }
    

    我们可以看到:

    • 1.如果name不是以&开头的,那么根本就不会进行转换,直接return
    • 2.如果名字是以&开头呢,就把名字前面的所有的&都去掉。

    我们这里就明白了,transfromedBeanName其实就是把名字做一个转换,如果名字前有&的都给去掉,比如我们之前的&myFactoryBean就在这里变成了myFactoryBean

    接着第二步

    Object sharedInstance = getSingleton(beanName);
    

    就是从三级缓存中拿对象(也就是我们背的八股文的循环依赖的解决方案的三级缓存)。但是我们这里根本就没有创建过myFactoryBean这个对象,从三级缓存中肯定是拿不到的,就要走后续的逻辑去创建myFactoryBean对象,创建的过程我们暂时不管,忽略掉。

    2.2.2 FactoryBeanObject的创建

    image.png

    然后就是判断,这个Bean是否是SmartFactoryBean,如果是的话,还要考虑是否isEagerInit,这个我们在之前已经讲述过,我们这里配置了true,因此这里会去执行getBean,传入的beanNamemyFactoryBean

    是不是很奇怪?按照我们刚刚的逻辑,已经创建了myFactoryBean这个对象了,而且还保存到bean对象当中了。这里还调用getBean干嘛,不是白干活吗?我们继续看doGetBean的逻辑。

            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }
    

    因为我们之前已经创建过了myFactoryBean对象,因此getSingleton就可以从三级缓存当中去拿到对象myFactoryBean,因此sharedInstance就拿到非空,就走到getObjectForBeanInstance这个方法当中去了,而FactoryBeanObject(user)的创建,就是在这个方法中去进行创建的,这也就是为什么我们要两次调用getBean的原因,因为在这里会触发不同的逻辑。

    我们下面就来看getObjectForBeanInstance方法的逻辑

        protected Object getObjectForBeanInstance(
                Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    
            // Don't let calling code try to dereference the factory if the bean isn't a factory.
            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;
            }
    
            // Now we have the bean instance, which may be a normal bean or a FactoryBean.
            // If it's a FactoryBean, we use it to create a bean instance, unless the
            // caller actually wants a reference to the factory.
            if (!(beanInstance instanceof FactoryBean)) {
                return beanInstance;
            }
    
            Object object = null;
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }
            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);
                }
                boolean synthetic = (mbd != null && mbd.isSynthetic());
                object = getObjectFromFactoryBean(factory, beanName, !synthetic);
            }
            return object;
        }
    

    下面,我们进行挨个步骤的拆解分析

    (1) getObjectFromFactoryBean步骤1-判断你调用getBean时传递的参数是否以&作为前缀

            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;
            }
    

    这个部分的目的是为了什么?它其实就是判断,name是否是以&开头的,如果是的话,直接return beanInstance,也就是直接return FactoryBean。如果name不是以&开头的,那么不会进入这里,直接进入后续的逻辑。(需要注意的是:这里传递的参数beanName一定是去掉了&的,而name是没有去掉&的原生beanName)

    (2) getObjectFromFactoryBean步骤2-判断getSingleton拿到的对象是否是FactoryBean

    接着是下面这段代码

            if (!(beanInstance instanceof FactoryBean)) {
                return beanInstance;
            }
    

    因为getSingleton返回非空的情况,都会进入这里,因此beanInstance有可能是一个FactoryBean,也可能就是一个普通的Bean,因此这里需要过滤掉所有的普通的Bean

    (3) getObjectFromFactoryBean步骤3-尝试从缓存当中去获取FactoryBeanObject

            Object object = null;
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }
            else {
                object = getCachedObjectForFactoryBean(beanName);
            }
    

    这里传递的mbdnull,因此走else逻辑,而这个else中调用的方法的意图,从名字上我们就很清晰的看到,是从缓存中根据FactoryBeanbeanName去拿到它导入的FactoryBeanObject。(具体代码为return this.factoryBeanObjectCache.get(beanName);)

    如果第一次来,这里很显然为拿不到,因此会走下面的逻辑;而如果已经创建过了,第二次来这里,直接就从缓存当中拿出来对象了。

    (4) getObjectFromFactoryBean步骤4-创建FactoryBeanObject并加入到缓存当中

            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);
                }
                boolean synthetic = (mbd != null && mbd.isSynthetic());
                object = getObjectFromFactoryBean(factory, beanName, !synthetic);
            }
            return object;
    

    这里意图很清晰嘛,就是调用getObjectFromFactoryBean去完成FactoryBeanObject对象的创建,具体的逻辑如下。

        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);
                        // Only post-process and store if not put there already during getObject() call above
                        // (e.g. because of circular reference processing triggered by custom getBean calls)
                        Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                        if (alreadyThere != null) {
                            object = alreadyThere;
                        }
                        else {
                            if (shouldPostProcess) {
                                if (isSingletonCurrentlyInCreation(beanName)) {
                                    // Temporarily return non-post-processed object, not storing it yet..
                                    return object;
                                }
                                beforeSingletonCreation(beanName);
                                try {
                                    object = postProcessObjectFromFactoryBean(object, beanName);
                                }
                                catch (Throwable ex) {
                                    throw new BeanCreationException(beanName,
                                            "Post-processing of FactoryBean's singleton object failed", ex);
                                }
                                finally {
                                    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(beanName, "Post-processing of FactoryBean's object failed", ex);
                    }
                }
                return object;
            }
        }
    

    我们可以看到这里的逻辑就是:

    • 1.如果是FactoryBeanObject是单例的,那么就加上锁,然后从缓存中拿对象,如果拿到了,那么就return,如果拿不到,就去调用FactoryBean.getObject方法去创建一个,然后再放入缓存当中。
    • 2.如果FactoryBean不是单例的?那么直接新创建一个并return就行了。

    我们还要来看看doGetObjectFromFactoryBean中是如何创建FactoryBeanObject的?

        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 {
                    object = factory.getObject();
                }
            }
            catch (FactoryBeanNotInitializedException ex) {
                throw new BeanCurrentlyInCreationException(beanName, ex.toString());
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
            }
    
            // Do not accept a null value for a FactoryBean that's not fully
            // initialized yet: Many FactoryBeans just return null then.
            if (object == null) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(
                            beanName, "FactoryBean which is currently in creation returned null from getObject");
                }
                object = new NullBean();
            }
            return object;
        }
    

    其实很简单,就是简单调用了一下FactoryBean.getObject方法罢了。

    (5) getObjectFromFactoryBean步骤5-对FactoryBeanObject的后置处理工作(postProcessObjectFromFactoryBean)

    在创建完FactoryBeanObject之后,我们的Bean只是完成了实例化,并未完成初始化工作,比如有些特定的Bean需要完成动态代理,就会在这里进行回调。

    postProcessObjectFromFactoryBean在子类当中的实现如下:

        protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
            return applyBeanPostProcessorsAfterInitialization(object, beanName);
        }
    

    接着往下看

        @Override
        public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
                throws BeansException {
    
            Object result = existingBean;
            for (BeanPostProcessor processor : getBeanPostProcessors()) {
                Object current = processor.postProcessAfterInitialization(result, beanName);
                if (current == null) {
                    return result;
                }
                result = current;
            }
            return result;
        }
    

    我们可以看到,它是遍历所有的BeanPostProcessor,执行它的postProcessAfterInitialization方法,也就是说BeanPostProcessor有机会在这里对FactoryBeanObject去完成动态代理,我们知道Spring AOP用到的BeanPostProcessor实现动态代理的逻辑,其实就是在postProcessAfterInitialization方法当中!

    3. FactoryBean的更多知识

    3.1 我们如何拿到FactoryBeanFactoryBeanObject

    我们通过上面的源码分析可以知道:

    • 1.如果你在getBean时使用了&作为前缀(比如&myFactoryBean),那么在getSingleton拿到了FactoryBean之后,就会因为&前缀,导致不会从缓存当中去获取FactoryBeanObject,因此拿到的就是FactoryBean对象本身(比如myFactoryBean)。
    • 2.如果你在getBean时没加前缀&(比如myFactoryBean),那么在getSingleton之后,就会因为拿到的这个对象是FactoryBean,就会直接从缓存中拿到FactoryBean导入的FactoryBeanObject对象(如果是懒加载的话,在第一次获取时,会先进行创建)。
    • 3.那么IOC容器(三级缓存)中究竟有没有user作为beanNameBean,我们可以从源码当中可以看到,创建和导入User对象时,都没有使用到user,都使用到的FactoryBean本身的beanName,因此很显然缓存当中并没有user作为beanName的对象,你通过usergetBean肯定是获取不到的。

    我们有很多种方式可以获取到FactoryBean/FactoryBeanObject

    直接注入ApplicationContext/BeanFactory(或者实现相应的XXXAware接口),然后通过ApplicationContext/BeanFactory去调用getBean获取

        @Autowired
        ApplicationContext applicationContext;
    
        @Autowired
        BeanFactory beanFactory;
    

    然后使用beanFactory.getBean("myFactoryBean")可以获取到我们创建的User对象,使用beanFactory.getBean("&myFactoryBean")可以获取到我们创建的FactoryBean对象本身,也就是myFactoryBean这个对象。

    当然,也可以直接@Qualifier直接进行注入

        @Autowired
        @Qualifier("myFactoryBean")
        User user;
    

    3.2 FactoryBean的应用?

    MyBatis中去导入SqlSessionFactory这个核心组件时,就使用到了SqlSessionFactoryBean,而这个对象就是FactoryBean,当然其实MyBatis中的MapperFactoryBean也是使用的FactoryBean。其实整个MyBatis就是通过SpringFactoryBean机制为容器中导入Bean,从而实现相关的功能的。

    个人博客:http://wanna1314y.top:8090/

    相关文章

      网友评论

        本文标题:Spring扩展点-FactoryBean

        本文链接:https://www.haomeiwen.com/subject/iiyaxrtx.html