当前位置: 移动技术网 > IT编程>开发语言>Java > Spring中FactoryBean的作用和实现原理

Spring中FactoryBean的作用和实现原理

2019年08月01日  | 移动技术网IT编程  | 我要评论

另类字,固始房产中介,美食杰家常菜谱大全

beanfactory与factorybean,相信很多刚翻看spring源码的同学跟我一样很好奇这俩货怎么长得这么像,分别都是干啥用的。beanfactory是spring中bean工厂的顶层接口,也是我们常说的springioc容器,它定下了ioc容器的一些规范和常用方法并管理着spring中所有的bean,今天我们不讲它,我们看一下后面那个factorybean。

先说下factorybean和其作用再开始分析:首先它是一个bean,但又不仅仅是一个bean。它是一个能生产或修饰对象生成的工厂bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何bean的实例。


首发地址:

上面的解释有点抽象,那么我们浏览一遍factorybean在spring中是怎么应用的就好懂了。我们都知道在spring中我们的bean都会被spring的ioc容器所管理,在abstractapplicationcontext中有一个很重要的方法:refresh(),项目启动或重启的时候refresh()会调用getbean()初始化所有的bean,这个getbean()最终会指向abstractbeanfactory中的getbean()方法。

在abstractbeanfactory中,不管是根据名称还是根据类型,getbean()最终都会调用dogetbean(),在dogetbean()方法中一开始就获取了名称beanname和实例sharedinstance,这个方法太长,这里就贴前面两行。

        string beanname = this.transformedbeanname(name);
        object sharedinstance = this.getsingleton(beanname);

transformedbeanname(name)是为了获取bean真正的名称,它会去掉name前面的'&',而getsingleton(beanname)是从父类容器singletonobjects中取的这个bean的实例。在spring中还有很多这样的容器,比如defaultlistablebeanfactory中的beandefinitionmap,它就是的ioc容器真正保存bean的地方,它是一个hashmap。类似的还有factorybeanregistrysupport中的factorybeanobjectcache等。

回到dogetbean()方法中,拿到sharedinstance后,后面的一大堆操作做了单例、多例等判断,最终会走到this.getobjectforbeaninstance(),关键部分就在这个方法中,进入方法代码。

protected object getobjectforbeaninstance(object beaninstance, string name, string beanname, 
                                          @nullable rootbeandefinition mbd) {
        if (beanfactoryutils.isfactorydereference(name)) {
            if (beaninstance instanceof nullbean) {
                return beaninstance;
            }
            if (!(beaninstance instanceof factorybean)) {
                throw new beanisnotafactoryexception(this.transformedbeanname(name), beaninstance.getclass());
            }
        }
        if (beaninstance instanceof factorybean && !beanfactoryutils.isfactorydereference(name)) {
            object object = null;
            if (mbd == null) {
                object = this.getcachedobjectforfactorybean(beanname);
            }
            if (object == null) {
                factorybean<?> factory = (factorybean)beaninstance;
                if (mbd == null && this.containsbeandefinition(beanname)) {
                    mbd = this.getmergedlocalbeandefinition(beanname);
                }
                boolean synthetic = mbd != null && mbd.issynthetic();
                object = this.getobjectfromfactorybean(factory, beanname, !synthetic);
            }
            return object;
        } else {
            return beaninstance;
        }
    }

在上面的代码中有两个判断分别是beaninstance instanceof factorybeanbeanfactoryutils.isfactorydereference(name),前面判断的是beaninstance是否属于factorybean或其子类的实例,后面这个方法判断name是否不为空且以&开头。

    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); 继续跟踪进去看代码。

protected object getobjectfromfactorybean(factorybean<?> factory, string beanname, boolean shouldpostprocess) {
        if (factory.issingleton() && this.containssingleton(beanname)) {
            synchronized(this.getsingletonmutex()) {
                object object = this.factorybeanobjectcache.get(beanname);
                if (object == null) {
                    object = this.dogetobjectfromfactorybean(factory, beanname);
                    object alreadythere = this.factorybeanobjectcache.get(beanname);
                    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;
        }
    }

这里面也是做了很多判断,咱们也只挑关键部分看。这里说一下个人想法,看源码的时候如果我们一直追究所有的细节那会让我们会越陷越深,掉入细节的无底洞,稍不留神脑回路跟不上就会蒙圈。我们要学会找源码中的关键部分看,弄懂主要流程和本次看源码的目的的那部分就行。等我们对spring整体有了一个很好的理解之后,再回头看之前不懂的代码就会豁然开朗。在上面这个方法中不管是走上面的if分支还是到下边的else中,关键部分就是object = this.dogetobjectfromfactorybean(factory, beanname)这段代码调用,继续点进去。

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的描述印证了,factorybean是一个能生产或修饰对象生成的工厂bean。一个bean如果实现了factorybean接口,那么根据该bean的名称获取到的实际上是getobject()返回的对象,而不是这个bean自身实例,如果要获取这个bean自身实例,那么需要在名称前面加上'&'符号

一般情况下,spring通过反射机制利用的class属性指定实现类实例化bean,在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。spring为此提供了一个org.springframework.bean.factory.factorybean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。factorybean接口对于spring框架来说占用重要的地位,spring自身就提供了70多个factorybean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从spring3.0开始,factorybean开始支持泛型,即接口声明改为factorybean的形式


原理弄明白了,下面通过代码测试验证上面的流程,先定义一个bean实现factorybean接口。

@component
public class mybean implements factorybean {
    private string message;
    public mybean() {
        this.message = "通过构造方法初始化实例";
    }
    @override
    public object getobject() throws exception {
        mybean mybean = new mybean();
        mybean.message = "通过factorybean.getobject()创建实例";
        // 这里并不一定要返回mybean自身的实例,可以是其他任何对象的实例
        return mybean;
    }
    @override
    public class<?> getobjecttype() {
        return mybean.class;
    }
    public string getmessage() {
        return message;
    }
}

mybean实现了factorybean接口的两个方法,getobject()是可以返回任何对象的实例的,这里测试就返回mybean自身实例,且返回前给message字段赋值。同时在构造方法中也为message赋值。然后测试代码中先通过名称获取bean实例,打印message的内容,再通过'&'+名称获取实例并打印message内容。

@runwith(springrunner.class)
@springboottest(classes = testapplication.class)
public class factorybeantest {
    @autowired
    private applicationcontext context;
    @test
    public void test() {
        mybean mybean1 = (mybean) context.getbean("mybean");
        system.out.println("mybean1 = " + mybean1.getmessage());
        mybean mybean2 = (mybean) context.getbean("&mybean");
        system.out.println("mybean2 = " + mybean2.getmessage());
        system.out.println("mybean1.equals(mybean2) = " + mybean1.equals(mybean2));
    }
}
mybean1 = 通过factorybean.getobject()初始化实例
mybean2 = 通过构造方法初始化实例
mybean1.equals(mybean2) = false

通过测试我们发现获取的两个实例中的message的值不一样,对比两个对象的引用也不相同。上述所讲关于factorybean的内容印证完毕,本文结束。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网