FactoryBean

FactoryBean是spring框架定義的一個接口. 用于自定義bean的整個實例化過程;

說到實例化,默認(rèn)spring bean只有給beanFactory注冊beanDefinition就可以自動完成實例化, 這種方式能滿足絕大多數(shù)的實例化需求. FactoryBean是為了滿足復(fù)雜實例化的需求,比如aop生成代理對象, 自定義接口給接口生成代理對象等卫袒。


image.png

用法.

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
// 定義beanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserComponentFactoryBean.class);
// 自動注入
builder.setAutowireMode(2);
annotationConfigApplicationContext.registerBeanDefinition("userComponentFactoryBean", builder.getBeanDefinition());
annotationConfigApplicationContext.refresh();
public class UserComponentFactoryBean implements FactoryBean<UserComponent> {
   private UserComponent userComponent;
   @Override
    public UserComponent getObject() throws Exception {
      if (userComponent == null) {
        userComponent = new UserComponent();
        System.out.println("------------ factoryBean----" + userComponent.hashCode());
       }       
       return userComponent;
    }
    @Override
    public Class<?> getObjectType() {
        return UserComponent.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

這里使用注冊beanDefinition的方式讓beanFactory感知到有FactoryBean该贾,當(dāng)然也可以用包掃描的方式加@Component诉稍。
這里UserComponentFactoryBean的getObject()方法獲取對象實例只是示范赶熟,并不規(guī)范茫负。
注意:如果UserCompontFactoryBean有依賴其他bean破停,F(xiàn)actoryBean不會自動完成注入翅楼。
如果需要獲取bean關(guān)聯(lián)的FactoryBean需要在beanName前加上 &

ProxyFactoryBean

spring中使用ProxyFactoryBean創(chuàng)建代理。
ProxyFactoryBean可以配置的屬性有.

  • proxyTargetClass: 如果要對類進(jìn)行代理真慢,這個屬性設(shè)置為true
  • optimize: 針對CGLIB優(yōu)化.
  • frozen: 凍結(jié)配置毅臊。如果為true有倆個作用,一個是不允許CGLIB優(yōu)化黑界, 另一個是代理創(chuàng)建后不允許手動進(jìn)行增強的添加修改管嬉。默認(rèn)是false, 所以一般情況下可以進(jìn)行增強的手動添加修改;
  • exposeProxy:是否暴露代理對象皂林。如果是true, 則在增強點/目標(biāo)對象可以通過AopContext.currentProxy()拿到代理對象;
  • proxyInterfaces: string對象數(shù)組, 用于指定代理對象實現(xiàn)的接口。如果有值且-proxyTargetClass=false將啟用jdk代理.
  • interceptoreNames: 數(shù)組, 聲明增強點集合蚯撩〈”叮可以用* 一次匹配多個增強點;
  • singleton: ProxyFactoryBean的getObject方法是否返回單例;

示例

  • 定義被代理對象
public class TargetBean {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getProxy() {
        // 在被代理對象里獲取 代理
        return AopContext.currentProxy();
    }
    @Override
    public String toString() {
        return "TargetBean{" +
                "name='" + name + '\'' +
                '}';
    }
}
  • 定義ProxyFactoryBean, 并設(shè)置一個增強點
@Configuration
public class FactoryBeanConfig {

    @Bean("debugInterceptor")
    public DebugInterceptor getInterceptor() {
        return new DebugInterceptor();
    }

    @Bean
    public ProxyFactoryBean getProxyFactoryBean() {
        ProxyFactoryBean pfb = new ProxyFactoryBean();
        pfb.setInterceptorNames("debugInterceptor");
        pfb.setTarget(new TargetBean());
        // 暴露代理對象
        pfb.setExposeProxy(true);
        return pfb;
    }
}
  • 啟動
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.scan("com.xie.java.aop.factorybean");
        applicationContext.refresh();

        TargetBean targetBean = applicationContext.getBean(TargetBean.class);
        System.out.println("target bean :" + targetBean);
        System.out.println("proxy bean :" + targetBean.getProxy());
        // 任何代理對象都可以強制轉(zhuǎn)換成 Advised對象
        Advised advised = (Advised) targetBean;
        System.out.println("advised :" + advised);
         for (Advisor ad : advised.getAdvisors()) {
             System.out.println("ad :" + ad );
             System.out.println(" count = " + ((DebugInterceptor) ad.getAdvice()).getCount());
          // 如果frozen = false, 可動態(tài)添加修改增強點
         }

以上示例可以看出, ProxyFactoryBean是spring bean對象胎挎,目標(biāo)類TargetBean不能是spring bean, 不然 getBean(TargetBean.class)會報異常沟启。
getBean(TargetBean.class)查找ProxyFactoryBean的流程如下
1、根據(jù)類型TargetBean.class 查詢spring容器中的所有bean Definition; 如果是FactoryBean則觸發(fā)getType方法犹菇。
代碼來自 DefaultListableBeanFactory

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
        List<String> result = new ArrayList<String>();

        // Check all bean definitions.
        for (String beanName : this.beanDefinitionNames) {
            // Only consider bean as eligible if the bean name
            // is not defined as alias for some other bean.
            if (!isAlias(beanName)) {
                try {
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    // Only check bean definition if it is complete.
                    if (!mbd.isAbstract() && (allowEagerInit ||
                            (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
                                    !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                        // In case of FactoryBean, match object created by FactoryBean.
                                     // 判斷bean Definition是否是FactoryBean類型
                        boolean isFactoryBean = isFactoryBean(beanName, mbd);
                        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                        boolean matchFound =
                                (allowEagerInit || !isFactoryBean ||
                                        (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
                                (includeNonSingletons ||
                                        (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
                                isTypeMatch(beanName, type);
                        if (!matchFound && isFactoryBean) {
                            // In case of FactoryBean, try to match FactoryBean instance itself next.
                            beanName = FACTORY_BEAN_PREFIX + beanName;
                            matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
                        }
                        if (matchFound) {
                            result.add(beanName);
                        }
                    }
                }
                ..... 此處省略

        // Check manually registered singletons too.
              // 檢查手動注冊的單例
        for (String beanName : this.manualSingletonNames) {
            try {
                // In case of FactoryBean, match object created by FactoryBean.
                        // 又一個FactoryBean判斷
                if (isFactoryBean(beanName)) {
                    if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
                        result.add(beanName);
                        // Match found for this bean: do not match FactoryBean itself anymore.
                        continue;
                    }
                    // In case of FactoryBean, try to match FactoryBean itself next.
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                }
                // Match raw bean instance (might be raw FactoryBean).
                if (isTypeMatch(beanName, type)) {
                    result.add(beanName);
                }
            }
            ... 此處省略
        }

        return StringUtils.toStringArray(result);
    }

2德迹、 找到ProxyFactoryBean后, 根據(jù)ProxyFactoryBean的 bean name 觸發(fā)getBean(name, type args)
找到ProxyFactoryBean的實例后 觸發(fā)getObjectForBeanInstance方法,從bean里獲取bean
代碼來著 AbstractBeanFactory

/**
     * Get the object for the given bean instance, either the bean
     * instance itself or its created object in case of a FactoryBean.
     * @param beanInstance the shared bean instance
     * @param name name that may include factory dereference prefix
     * @param beanName the canonical bean name
     * @param mbd the merged bean definition
     * @return the object to expose for the bean
     */
    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

        // Don't let calling code try to dereference the factory if the bean isn't a factory.
        if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }

        // 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) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

        Object object = null;
        if (mbd == null) {
        // 此處 觸發(fā)從FactoryBean 緩存 獲取 對象
            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());
        // 觸發(fā)直接從FactoryBean 獲取對象
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }

3揭芍、觸發(fā)ProxyFactory的 getObject()方法

public Object getObject() throws BeansException {
        initializeAdvisorChain();
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }

總結(jié)ProxyFactoryBean到目標(biāo)代理對象的流程胳搞。
1、spring根據(jù)getBean的目標(biāo)類型查找BeanDefinition
2称杨、如果BeanDefinition是FactoryBean肌毅,則觸發(fā)其getType方法
3、拿到ProxyFactoryBean bean后列另, 最終觸發(fā)FactoryBean的getObject方法拿到代理對象.

MyBatise在FactoryBean上的應(yīng)用

MapperFactoryBean<T>

程序急需要定義Mapper接口芽腾,MapperFactoryBean就會為其生成代理對象.

Dubbo在FactoryBean上的應(yīng)用

ReferenceBean<T>

dubbo會為標(biāo)識為@Reference的注入使用javassist生成具體的bean.

Spring Context在FactoryBean上的應(yīng)用

ScopedProxyFactoryBean

給聲明了@RefreshScope的類生成代理, 處理配置實時刷新同步的問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市页衙,隨后出現(xiàn)的幾起案子摊滔,更是在濱河造成了極大的恐慌,老刑警劉巖店乐,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艰躺,死亡現(xiàn)場離奇詭異,居然都是意外死亡眨八,警方通過查閱死者的電腦和手機(jī)腺兴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廉侧,“玉大人页响,你說我怎么就攤上這事《翁埽” “怎么了闰蚕?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長连舍。 經(jīng)常有香客問我没陡,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任盼玄,我火速辦了婚禮贴彼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘埃儿。我一直安慰自己器仗,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布蝌箍。 她就那樣靜靜地躺著青灼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妓盲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天专普,我揣著相機(jī)與錄音悯衬,去河邊找鬼。 笑死檀夹,一個胖子當(dāng)著我的面吹牛筋粗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炸渡,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼娜亿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚌堵?” 一聲冷哼從身側(cè)響起买决,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吼畏,沒想到半個月后督赤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡泻蚊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年躲舌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片性雄。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡没卸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秒旋,到底是詐尸還是另有隱情约计,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布滩褥,位于F島的核電站病蛉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铺然,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一俗孝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧魄健,春花似錦赋铝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至析恋,卻和暖如春良哲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背助隧。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工筑凫, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人并村。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓巍实,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哩牍。 傳聞我的和親對象是個殘疾皇子棚潦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容