国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring源碼:Bean生命周期(三)

這篇具有很好參考價值的文章主要介紹了Spring源碼:Bean生命周期(三)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

在之前的文章中,我們已經對 bean 的準備工作進行了講解,包括 bean 定義和 FactoryBean 判斷等。在這個基礎上,我們可以更加深入地理解 getBean 方法的實現邏輯,并在后續(xù)的學習中更好地掌握createBean 方法的實現細節(jié)。

getBean用法

講解getBean方法之前,我們先來看看他有幾種常見的用法:

// 創(chuàng)建一個Spring容器  
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);  
UserService bean1 = applicationContext.getBean(UserService.class);  
UserService bean2 = (UserService)applicationContext.getBean("userService");  
UserService bean3 = applicationContext.getBean("userService",UserService.class);  
UserService bean4 = (UserService) applicationContext.getBean("userService",new OrderService());  
bean1.test();  
bean2.test();  
bean3.test();  
bean4.test();

關于獲取 bean 的方法,前兩種方法應該比較常見,這里就不再贅述。第三種方法實際上是在獲取 bean 的時候,會先判斷是否符合指定的類型,如果符合,則進行類型轉換并返回對應的 bean 實例。第四種方法則是在創(chuàng)建 bean 實例時,通過推斷構造方法的方式來選擇使用帶有參數的構造方法進行實例化。

如果我們想要讓第四種方法生效,可以考慮使用多例的形式,即通過設置 scope 屬性為 prototype 來實現。這樣,每次獲取 bean 時,都會創(chuàng)建新的 bean 實例,從而可以觸發(fā)使用帶有參數的構造方法進行實例化,比如這樣:

@Component  
@Scope("prototype")  
public class UserService {  
  
     public UserService(){  
      System.out.println(0);  
   }  
     public UserService(OrderService orderService){  
      System.out.println(1);  
   }  
   public void test(){  
      System.out.println(11);  
   }  
}

getBean大體流程

由于方法代碼太多,我就不貼代碼了,我這邊只貼一些主要的偽代碼,方便大家閱讀,然后我在對每個流程細講下:

	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		// name有可能是 &xxx 或者 xxx,如果name是&xxx,那么beanName就是xxx
		// name有可能傳入進來的是別名,那么beanName就是id
		String beanName = transformedBeanName(name);
		Object beanInstance;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			
			// 如果sharedInstance是FactoryBean,那么就調用getObject()返回對象			
		}

		else {	
			//檢查是否本beanfactory沒有當前bean定義,查看有父容器,如果有,則調用父容器的getbean方法				  
			try {				 
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// 檢查BeanDefinition是不是Abstract的
				checkMergedBeanDefinition(mbd, beanName, args);
				// Guarantee initialization of beans that the current bean depends on.				 
				//查看是否有dependsOn注解,如果存在循環(huán)依賴則報錯
				// Create bean instance.
				if (mbd.isSingleton()) {
					//調用createBean方法
					//如果是FactoryBean則調用getObject
				}
				else if (mbd.isPrototype()) {
					//調用createBean方法,與單例只是前后邏輯不一樣
					//如果是FactoryBean則調用getObject
				}
				else {
					Scope不同類型有不同實現
					//調用createBean方法,與單例只是前后邏輯不一樣
					//如果是FactoryBean則調用getObject
				}
			}catch{
				......
			}
		}

		// 檢查通過name所獲得到的beanInstance的類型是否是requiredType
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

單例緩存池

在 Spring 中,不管傳入的 beanName 是多例的還是單例的,都會先從單例緩存池中獲取。有些人可能會覺得這樣做會浪費一些性能,但實際上 Spring 考慮到了大部分托管的 bean 都是單例的情況,因此忽略了這一點性能。實際上,這樣的性能消耗并不大??梢詫⑵漕惐扔?Java 的雙親委派機制,都會先查看本加載器是否有緩存,如果沒有再向父加載器去加載。

parentBeanFactory

在分析 bean 定義是如何創(chuàng)建的時,我們可以不考慮單例緩存池中獲取對象的情況,而是逐步分析 bean 定義是如何創(chuàng)建的。在這個過程中,即使存在 parentBeanFactory,我們也可以跳過它,因為我們的啟動容器并沒有設置任何父容器。源碼也很簡單,如果本容器沒有 bean 定義,就直接調用父容器的 getBean 相關方法:

BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                // &&&&xxx---->&xxx
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else if (requiredType != null) {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }

dependsOn

在調用 getBean 方法之前,已經將合并的 bean 定義存入了容器中。因此,我們可以直接獲取已經合并好的 bean 定義,并解析 bean 定義上的 dependsOn 注解。具體的源碼邏輯如下:

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

                // 檢查BeanDefinition是不是Abstract的
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    // dependsOn表示當前beanName所依賴的,當前Bean創(chuàng)建之前dependsOn所依賴的Bean必須已經創(chuàng)建好了
                    for (String dep : dependsOn) {
                        // beanName是不是被dep依賴了,如果是則出現了循環(huán)依賴
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        // dep被beanName依賴了,存入dependentBeanMap中,dep為key,beanName為value
                        registerDependentBean(dep, beanName);

                        // 創(chuàng)建所依賴的bean
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }

這里的邏輯還是相對簡單的。如果當前 bean 被 dependsOn 注解所依賴,那么會先去創(chuàng)建所依賴的 bean。但是這種方式是解決不了循環(huán)依賴的問題的。在實現上,只使用了兩個 Map 進行判斷:

// 某個Bean被哪些Bean依賴了
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
// 某個Bean依賴了哪些Bean
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

isSingleton

sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

在這個階段,我們可以看到代碼已經在準備創(chuàng)建單例 bean 實例了,因此我們可以不去深入理解這部分的源碼邏輯。反正,在 getSingleton 方法中,會調用 createBean 方法。這里使用了 lambda 表達式,如果有不太了解的讀者,可以參考下之前發(fā)的文章進行學習:

isPrototype

  if (mbd.isPrototype()) {
			// It's a prototype -> create a new instance.
			Object prototypeInstance = null;
			try {
				beforePrototypeCreation(beanName);
				prototypeInstance = createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
			beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
		}

在這個階段,我們發(fā)現創(chuàng)建 bean 的方法已經改變了,直接調用了 createBean 方法,而不是通過 getSingleton 方法進行調用。至于 beforePrototypeCreation 和 afterPrototypeCreation,我們可以不用管它們,因為它們只是存儲一些信息,對我們創(chuàng)建 bean 并沒有太大的影響。

其他Scope

講解這部分源碼之前,我們先來看看還有哪些Scope域:

//@RequestScope
@SessionScope
public class User {
}

現在我們來看一下 RequestScope 和 SessionScope,它們與其他作用域類似,只是一個組合注解。它們的元注解信息如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

	/**
	 * Alias for {@link Scope#proxyMode}.
	 * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
	 */
	@AliasFor(annotation = Scope.class)
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

然后我們再來看下Spring對其他Scope注解的邏輯判斷:

String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean ′" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {  // session.getAttriute(beaName)  setAttri
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}

其實他主要用的getAttriute方法,我們看下scope.get主要的邏輯判斷:

	public Object get(String name, ObjectFactory<?> objectFactory) {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		Object scopedObject = attributes.getAttribute(name, getScope());
		if (scopedObject == null) {
			scopedObject = objectFactory.getObject();
			attributes.setAttribute(name, scopedObject, getScope());
			// Retrieve object again, registering it for implicit session attribute updates.
			// As a bonus, we also allow for potential decoration at the getAttribute level.
			Object retrievedObject = attributes.getAttribute(name, getScope());
			if (retrievedObject != null) {
				// Only proceed with retrieved object if still present (the expected case).
				// If it disappeared concurrently, we return our locally created instance.
				scopedObject = retrievedObject;
			}
		}
		return scopedObject;
	}

在這個階段,我們可以看到,通過 objectFactory.getObject() 方法,會調用外層定義的 lambda 表達式,也就是 createBean 方法的邏輯。假設這個過程成功地創(chuàng)建了 bean 實例,并返回了它,那么 Spring 會調用 setAttribute 方法,將這個 bean 實例以及其 scope 值放入以 beanName 為 key 的屬性中。這樣,當需要獲取這個 bean 實例時,Spring 就可以直接從作用域中獲取了。

結語

getBean 方法主要包含以下幾個步驟:

  1. 首先,從單例緩存池中獲取 bean 實例。如果沒有,Spring 會創(chuàng)建新的 bean 實例,并將其添加到單例緩存池中。
  2. 接著,Spring 會檢查當前容器是否有指定名稱的 bean 定義。如果沒有,Spring 會調用父容器的 getBean 方法,直到找到為止。
  3. 一旦找到了 bean 定義,Spring 會根據不同的作用域類型,創(chuàng)建對應的 bean 實例,并將其存儲在作用域中。
  4. 最后,Spring 會返回創(chuàng)建好的 bean 實例。

非常好,這樣我們對 getBean 方法的邏輯判斷有了一個大體的了解,有助于我們更好地理解 createBean 方法的實現細節(jié)。如果在后續(xù)的學習中有任何問題或疑問,可以隨時聯系我進行咨詢文章來源地址http://www.zghlxwxcb.cn/news/detail-433641.html

到了這里,關于Spring源碼:Bean生命周期(三)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Spring源碼:bean的生命周期(一)

    Spring源碼:bean的生命周期(一)

    本節(jié)將正式介紹Spring源碼細節(jié),將講解Bean生命周期。請注意,雖然我們不希望過于繁瑣地理解Spring源碼,但也不要認為Spring源碼很簡單。在本節(jié)中,我們將主要講解Spring 5.3.10版本的源代碼。如果您看到的代碼與我講解的不同,也沒有關系,因為其中的原理和業(yè)務邏輯基本相

    2024年02月02日
    瀏覽(23)
  • 【Spring源碼】講講Bean的生命周期

    【Spring源碼】講講Bean的生命周期

    面試官:“看過Spring源碼吧,簡單說說Spring中Bean的生命周期” 大神仙:“基本生命周期會經歷實例化 - 屬性賦值 - 初始化 - 銷毀”。 面試官:“......” 如果是普通Bean的生命周期,那么上述的回答是真正確的。確實會經歷“實例化 - 屬性賦值 - 初始化 - 銷毀”四個階段。但

    2023年04月09日
    瀏覽(20)
  • 【Spring】Spring之Bean生命周期源碼解析

    什么是bean的生命周期 是指bean在spring中是如何生成,如何銷毀的; spring創(chuàng)建對象的過程,就是IOC(控制反轉)的過程; JFR Java Flight Record,java飛行記錄,類似于飛機的黑匣子,是JVM內置的基于事件的JDK監(jiān)控記錄框架,主要用于問題定位和持續(xù)監(jiān)控; 入口代碼: Spring啟動的時

    2024年02月15日
    瀏覽(23)
  • 【框架源碼】Spring源碼解析之Bean生命周期流程

    【框架源碼】Spring源碼解析之Bean生命周期流程

    觀看本文前,我們先思考一個問題,什么是Spring的bean的生命周期?這也是我們在面試的時候,面試官常問的一個問題。 在沒有Spring之前,我們創(chuàng)建對象的時候,采用new的方式,當對象不在被使用的時候,由Java的垃圾回收機制回收。 而 Spring 中的對象是 bean,bean 和普通的 J

    2024年02月09日
    瀏覽(21)
  • 【深入Spring源碼解析:解密Bean的生命周期】

    Spring是Java企業(yè)級應用開發(fā)領域的一顆明星,它提供了很多方便開發(fā)人員的工具和思想。在分布式系統(tǒng)中,Spring的分布式遠程協(xié)作方案,比如REST、Web服務以及消息傳遞等,也是不可或缺的。 你知道嗎?在我們使用Spring時,容器中存放的所有對象,在Spring啟動的時候就完成了實

    2024年02月05日
    瀏覽(28)
  • 【Spring專題】Spring之Bean的生命周期源碼解析——上(掃描生成BeanDefinition)

    【Spring專題】Spring之Bean的生命周期源碼解析——上(掃描生成BeanDefinition)

    由于Spring源碼分析是一個前后聯系比較強的過程,而且這邊分析,也是按照代碼順序講解的,所以不了解前置知識的情況下,大概率沒辦法看懂當前的內容。所以,特別推薦看看我前面的文章(自上而下次序): Spring底層核心原理解析——引導篇【學習難度: ★★☆☆☆ 】

    2024年02月13日
    瀏覽(22)
  • 【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition)

    【Spring專題】Spring之Bean的生命周期源碼解析——階段一(掃描生成BeanDefinition)

    由于Spring源碼分析是一個前后聯系比較強的過程,而且這邊分析,也是按照代碼順序講解的,所以不了解前置知識的情況下,大概率沒辦法看懂當前的內容。所以,特別推薦看看我前面的文章(自上而下次序): Spring底層核心原理解析——引導篇【學習難度: ★★☆☆☆ 】

    2024年02月13日
    瀏覽(24)
  • 【Spring專題】Spring之Bean的生命周期源碼解析——階段二(IOC之實例化)

    【Spring專題】Spring之Bean的生命周期源碼解析——階段二(IOC之實例化)

    由于Spring源碼分析是一個前后聯系比較強的過程,而且這邊分析,也是按照代碼順序講解的,所以不了解前置知識的情況下,大概率沒辦法看懂當前的內容。所以,特別推薦看看我前面的文章(自上而下次序): Spring底層核心原理解析——引導篇【學習難度: ★★☆☆☆ 】

    2024年02月13日
    瀏覽(37)
  • 【Spring專題】Spring之Bean的生命周期源碼解析——階段二(二)(IOC之屬性填充/依賴注入)

    【Spring專題】Spring之Bean的生命周期源碼解析——階段二(二)(IOC之屬性填充/依賴注入)

    由于Spring源碼分析是一個前后聯系比較強的過程,而且這邊分析,也是按照代碼順序講解的,所以不了解前置知識的情況下,大概率沒辦法看懂當前的內容。所以,特別推薦看看我前面的文章(自上而下次序): Spring底層核心原理解析【學習難度: ★★☆☆☆ 】 手寫簡易

    2024年02月12日
    瀏覽(25)
  • 【Spring】—— bean生命周期

    1、初始化容器 1)創(chuàng)建對象(分配內存) 2)執(zhí)行構造方法 3)執(zhí)行屬性注入(set操作) 4)執(zhí)行bean初始化方法 2、使用bean 1)執(zhí)行業(yè)務操作 3、關閉/銷毀容器 1)執(zhí)行bean銷毀方法

    2024年02月02日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包