最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring @Lookup深入分析實現(xiàn)原理

 更新時間:2023年01月03日 09:38:48   作者:程序員小潘  
這篇文章主要介紹了Spring @Lookup實現(xiàn)原理,我們知道在spring容器中單獨的一個抽象類是不能成為一個bean的,那么有沒有辦法呢?這個時候我們可以使用Lookup注解

1. 前言

在使用Spring的時候,往單例bean注入原型bean時,原型bean可能會失效,如下:

@Component
public class Person {
    @Autowired
    Car car;
    public Car getCar() {
        return car;
    }
}
@Component
@Scope("prototype")
public class Car {
}

調(diào)用Person#getCar()方法返回的總是同一個Car對象,這也很好理解,因為Person是單例的,Spring在創(chuàng)建Person時只會注入一次Car對象,以后Car都不會再改變了。

怎么解決這個問題呢?Spring提供了多種方式來獲取原型bean。

2. 解決方案

解決方案有很多,本文重點分析@Lookup注解的方式。

1、每次從ApplicationContext重新獲取bean。

@Component
public class Person implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Lookup
    public Car getCar() {
        return applicationContext.getBean(Car.class);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

2、通過CGLIB生成Car子類代理對象,每次都從容器內(nèi)獲取bean執(zhí)行。

@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Car {
}

3、通過**@Lookup**注解的方式,方法體已經(jīng)不重要了,代理對象每次都會從容器中重新獲取bean。

@Component
public class Person {
    @Lookup
    public Car getCar() {
        return null;
    }
}

3. 源碼分析

為什么方法上加了@Lookup注解,調(diào)用該方法就能拿到原型bean了呢?其實縱觀上述三種方式,要想拿到原型bean,底層原理都是一樣的,那就是每次都通過ApplicationContext#getBean()方法從容器中重新獲取,只要Car本身是原型的,Spring就會保證每次拿到的都是新創(chuàng)建的Car實例。

**@Lookup**注解也是通過生成代理類的方式,重寫被標(biāo)記的方法,每次都從ApplicationContext獲取bean。

1、Spring加載Person的時候,容器內(nèi)不存在該bean,那首先就是要實例化Person對象。Spring會通過SimpleInstantiationStrategy#instantiate()方法去實例化Person。

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    /**
     * 如果bean沒有要重寫的方法,直接反射調(diào)用構(gòu)造函數(shù)創(chuàng)建對象
     * 反之,需要通過CGLIB創(chuàng)建增強子類代理對象
     */
    if (!bd.hasMethodOverrides()) {
        Constructor<?> constructorToUse;
        synchronized (bd.constructorArgumentLock) {
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    if (System.getSecurityManager() != null) {
                        constructorToUse = AccessController.doPrivileged(
                                (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                    } else {
                        constructorToUse = clazz.getDeclaredConstructor();
                    }
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                } catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        return BeanUtils.instantiateClass(constructorToUse);
    } else {
        /**
         * 存在 lookup-method 和 replaced-method
         * 通過CGLIB生成代理類
         */
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

BeanDefinition有一個屬性**methodOverrides**,它里面存放的是當(dāng)前bean里面是否有需要被重寫的方法,這些需要被重寫的方法可能是**lookup-method****replaced-method**。如果沒有需要重寫的方法,則直接通過反射調(diào)用構(gòu)造函數(shù)來實例化對象;如果有需要重寫的方法,這個時候就不能直接實例化對象了,需要通過CGLIB來創(chuàng)建增強子類,把父類的方法給重寫掉。

于是會調(diào)用instantiateWithMethodInjection()方法來實例化bean,最終是通過CglibSubclassCreator來實例化。

@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
        @Nullable Constructor<?> ctor, Object... args) {
    // Must generate CGLIB subclass...
    return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
}

2、CglibSubclassCreator創(chuàng)建CGLIB子類,重寫父類方法。Spring會通過Enhancer來創(chuàng)建增強子類,被@Lookup標(biāo)記的方法會被LookupOverrideMethodInterceptor攔截。

public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
    Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
    Object instance;
    if (ctor == null) {
        instance = BeanUtils.instantiateClass(subclass);
    }
    else {
        try {
            Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
            instance = enhancedSubclassConstructor.newInstance(args);
        }
        catch (Exception ex) {
            throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
                    "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
        }
    }
    Factory factory = (Factory) instance;
    factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
            new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
            new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
    return instance;
}

3、LookupOverrideMethodInterceptor要攔截被@Lookup標(biāo)記的方法,必然要實現(xiàn)intercept()方法。邏輯很簡單,就是每次都調(diào)用BeanFactory#getBean()從容器中獲取bean。

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    // Cast is safe, as CallbackFilter filters are used selectively.
    LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
    Assert.state(lo != null, "LookupOverride not found");
    Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
    if (StringUtils.hasText(lo.getBeanName())) {
        return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                this.owner.getBean(lo.getBeanName()));
    }
    else {
        return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
                this.owner.getBean(method.getReturnType()));
    }
}

4. 總結(jié)

一個bean一旦擁有被@Lookup注解標(biāo)記的方法,就意味著該方法需要被重寫掉,Spring在實例化bean的時候會自動基于CGLIB生成增強子類對象重寫掉父類方法。此時父類被@Lookup注解標(biāo)記的方法體已經(jīng)不重要了,不會被執(zhí)行了,CGLIB子類會通過LookupOverrideMethodInterceptor攔截掉被@Lookup注解標(biāo)記的方法。方法體重寫的邏輯也很簡單,就是每次都通過BeanFactory獲取bean,只要bean本身是原型的,每次拿到的都將是不同的實例。

到此這篇關(guān)于Spring @Lookup深入分析實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Spring @Lookup內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實現(xiàn)分布式任務(wù)調(diào)度的詳細(xì)步驟

    SpringBoot實現(xiàn)分布式任務(wù)調(diào)度的詳細(xì)步驟

    隨著互聯(lián)網(wǎng)應(yīng)用的規(guī)模和復(fù)雜度不斷增加,單節(jié)點任務(wù)調(diào)度系統(tǒng)已經(jīng)難以滿足高并發(fā)、大數(shù)據(jù)量的處理需求,分布式任務(wù)調(diào)度成為了解決這一問題的重要手段,本文將介紹如何在Spring Boot中實現(xiàn)分布式任務(wù)調(diào)度,需要的朋友可以參考下
    2024-08-08
  • JAVA正則表達(dá)式及字符串的替換與分解相關(guān)知識總結(jié)

    JAVA正則表達(dá)式及字符串的替換與分解相關(guān)知識總結(jié)

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識總結(jié),文章圍繞著JAVA正則表達(dá)式及字符串的替換與分解展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java 實現(xiàn)簡單靜態(tài)資源Web服務(wù)器的示例

    Java 實現(xiàn)簡單靜態(tài)資源Web服務(wù)器的示例

    這篇文章主要介紹了Java 實現(xiàn)簡單靜態(tài)資源Web服務(wù)器的示例,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-11-11
  • 基于JSON和java對象的互轉(zhuǎn)方法

    基于JSON和java對象的互轉(zhuǎn)方法

    下面小編就為大家?guī)硪黄贘SON和java對象的互轉(zhuǎn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Spring Boot集成Resilience4J實現(xiàn)限流/重試/隔離

    Spring Boot集成Resilience4J實現(xiàn)限流/重試/隔離

    在Java的微服務(wù)生態(tài)中,對于服務(wù)保護(hù)組件,像springcloud的Hystrix,springcloud?alibaba的Sentinel,以及當(dāng)Hystrix停更之后官方推薦使用的Resilience4j,所以本文給大家介紹了Spring Boot集成Resilience4J實現(xiàn)限流/重試/隔離,需要的朋友可以參考下
    2024-03-03
  • 告別無盡等待:Java中的輪詢終止技巧

    告別無盡等待:Java中的輪詢終止技巧

    在Java中,輪詢是一種常見的處理方式,用于檢查某個條件是否滿足,直到滿足條件或達(dá)到一定的時間限制,本文將介紹Java中常用的輪詢結(jié)束方式,包括使用循環(huán)、定時器和線程池等方法,需要的朋友可以參考下
    2023-10-10
  • Java?Servlet異步請求開啟的簡單步驟

    Java?Servlet異步請求開啟的簡單步驟

    Java servlet是大家公認(rèn)的服務(wù)器端web技術(shù)的標(biāo)準(zhǔn),包括jsp,jsf,和大量的web框架,soap,RESTful web service api,還有新聞供應(yīng),下面這篇文章主要給大家介紹了關(guān)于Java?Servlet異步請求開啟的簡單步驟,需要的朋友可以參考下
    2022-02-02
  • 使用Java程序模擬實現(xiàn)新冠病毒傳染效果

    使用Java程序模擬實現(xiàn)新冠病毒傳染效果

    這篇文章主要介紹了用Java程序模擬實現(xiàn)新冠病毒傳染效果,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot集成Spring Security用JWT令牌實現(xiàn)登錄和鑒權(quán)的方法

    SpringBoot集成Spring Security用JWT令牌實現(xiàn)登錄和鑒權(quán)的方法

    這篇文章主要介紹了SpringBoot集成Spring Security用JWT令牌實現(xiàn)登錄和鑒權(quán)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Mybatis之@MapKey的實現(xiàn)

    Mybatis之@MapKey的實現(xiàn)

    本文介紹了Mybatis中@MapKey注解的使用場景與效果,包含使用@MapKey和不使用@MapKey注解的區(qū)別,然后通過源碼解析產(chǎn)生各種結(jié)果的原因,具有一定的參考價值,感興趣的可以了解一下
    2024-09-09

最新評論

蓬莱市| 巴南区| 盘山县| 苏尼特右旗| 盈江县| 宝鸡市| 南木林县| 莱州市| 宣汉县| 武平县| 东港市| 土默特右旗| 南京市| 通江县| 蓝田县| 绥阳县| 宣威市| 勐海县| 盐边县| 布拖县| 利川市| 礼泉县| 揭东县| 平乐县| 江门市| 石景山区| 广平县| 白河县| 寻乌县| 南澳县| 邹城市| 岱山县| 吉木乃县| 科技| 贞丰县| 昆山市| 庆元县| 广德县| 铜梁县| 岑溪市| 阿拉善右旗|