夯實Spring系列|第十二章:Spring Bean 生命週期-上篇

夯實Spring系列|第十二章:Spring Bean 生命週期-上篇

本章說明

本文應該是講 Spring Bean 生命週期最全的一篇,一共細分爲 18 個階段,從 Bean 的配置階段到最終的銷燬階段,還特別加入了垃圾回收,Spring 設計的各個階段的切入點都會演示到,而且都會進行源碼調試;由於章節太多,所以本章將分爲上中下三篇進行發佈。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0w2ICEnV-1588318851630)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200429125110856.png)]
上篇:主要是討論 Spring Bean 的元信息配置,解析,註冊,合併,類加載 5 個階段,其中後 4 個階段主要是 Spring 容器內部操作,我們無法進行編碼,所以源碼分析和調試較多,最好在 Idea 中跟着一起進行調試會比較好理解。

中篇:主要是討論 Spring 如何將 Class 進行實例化,以及實例化之後的屬性賦值階段,每個階段均有對應的接口回調方法進行演示。

下篇:主要討論初始化、銷燬以及垃圾回收等 3 個階段,以及每個階段對應的接口回調方法。

友情提醒:開始前請備好暈車藥~

1.項目環境

2.Spring Bean 元信息配置階段

BeanDefinition 配置

  • 面向資源
    • XML 配置
      • <bean id="…" …>
    • Properties 資源配置
  • 面向註解
    • @Configuration、@Component、@Bean
  • 面向 API
    • BeanDefinitionBuilder

2.1 XML 配置

源碼位置:ioc-container-overview 模塊

com.huajie.thinking.in.spring.ioc.overview.container.BeanFactoryAsIocContainerDemo

通過 XmlBeanDefinitionReader 來加載 XML 配置

/**
 * IOC 容器示例
 */
public class BeanFactoryAsIocContainerDemo {
    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 加載配置
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        String location = "classpath:/META-INF/dependency-injection-context.xml";
        int beanDefinitionsCount = reader.loadBeanDefinitions(location);

        System.out.println("Bean 定義加載的數量:"+beanDefinitionsCount);
        lookupCollectionByType(beanFactory);
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listBeanFactory.getBeansOfType(User.class);
            System.out.println("查找到的所有集合對象---" + users);
        }
    }
}

執行結果

Bean 定義加載的數量:4
查找到的所有集合對象---{user=User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}, superUser=SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}}

2.2 註解 配置

這個相關的示例太多了,前面的章節有很多,這裏就不演示了。

第六章:Spring Bean 註冊、實例化、初始化、銷燬 2.1.1 Java 註解配置元信息

2.3 Properties 資源配置

源碼位置:org.springframework.beans.factory.support.PropertiesBeanDefinitionReader

Java Doc 中有相應的示例

同樣我們在 resource/META-INF 目錄下面 新建一個 user.properties 文件

user 相當於 xml 中的 id,user.(class) 相當於 xml 中的 class,以此類推。

user.(class) = com.huajie.thinking.in.spring.ioc.overview.domain.User
user.id = 001
user.name = 小仙
user.city = WUHAN

使用 PropertiesBeanDefinitionReader 來加載這個 properties 文件

/**
 * Bean 生命週期 示例
 */
public class BeanMetadataConfigurationDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(beanFactory);
        String location = "/META-INF/user.properties";

        Resource resource = new ClassPathResource(location);
        EncodedResource encodedResource = new EncodedResource(resource,"GBK");
        int beanDefinitionsCount = reader.loadBeanDefinitions(encodedResource);
        System.out.println("Bean 定義加載的數量:"+beanDefinitionsCount);
        lookupCollectionByType(beanFactory);
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listBeanFactory.getBeansOfType(User.class);
            System.out.println("查找到的所有集合對象---" + users);
        }
    }
}

執行結果:

Bean 定義加載的數量:1
查找到的所有集合對象---{user=User{beanName='user', id=1, name='小仙', age=null, configFileReource=null, city=WUHAN, cities=null, lifeCities=null}}

2.4 面向 API

第五章:Spring Bean 定義 4.BeanDefinition 構建 小節

3.Spring Bean 元信息解析階段

  • 面向資源 BeanDefinition 解析
    • BeanDefinitionReader
    • Xml 解析器 - BeanDefintionParser
  • 面向註解 BeanDefinition 解析
    • AnnotatedBeanDefinitonReader

3.1 面向資源 BeanDefinition 解析

這兩種方式就是 2.Spring Bean 元信息配置階段 中的 xml 和 properties 文件。

3.2 面向註解 BeanDefinition 解析

這裏並不是演示 @Component 以及其派生註解的使用,而是演示 AnnotatedBeanDefinitionReader 這類如何解析註冊一個類,換言之 Spring 如何使用 AnnotatedBeanDefinitionReader 將標註有 @Component 的類註冊爲 BeanDefintion 。

/**
 * 註解解析 BeanDefinition 示例
 */
public class AnnotatedBeanDefinitionParserDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
        int beanDefinitionCountBefore = beanFactory.getBeanDefinitionCount();
        //註冊當前類(非 @Component class)
        reader.register(AnnotatedBeanDefinitionParserDemo.class);
        reader.register(Test.class);
        int beanDefinitionCountAfter = beanFactory.getBeanDefinitionCount();
        int beanDefinitionCount = beanDefinitionCountAfter - beanDefinitionCountBefore;
        System.out.println("Bean 定義註冊的數量:"+beanDefinitionCount);
        //Bean 名稱生成來自於 BeanNameGenerator,註冊實現 AnnotatedBeanNameGenerator
        AnnotatedBeanDefinitionParserDemo demo = beanFactory.getBean("annotatedBeanDefinitionParserDemo",
                AnnotatedBeanDefinitionParserDemo.class);

        System.out.println(demo);
    }

    public class Test{

    }
}

執行結果

Bean 定義註冊的數量:2
com.huajie.thinking.in.spring.bean.lifecycle.AnnotatedBeanDefinitionParserDemo@31f924f5

4.Spring Bean 註冊階段

BeanDefintion 註冊接口

  • BeanDefinitionRegistry

唯一實現

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

源碼較多,這裏省略一部分,只對重點代碼進行分析

  • beanDefinitionMap 數據結構爲 ConcurrentHashMap,沒有順序
  • beanDefinitionNames 數據結構爲 ArrayList, 存放 beanName 保證註冊的順序
  • this.beanDefinitionMap.put(beanName, beanDefinition); //將 beanDefinition 存入 beanDefinitionMap
  • removeManualSingletonName,註冊的單例對象(並非 Bean Scope)和註冊 BeanDefinition 是一個互斥的操作,只能存在一個

省略後部分源碼如下:

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
        ...
        // beanDefinition 效驗
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
        ...
        //是否已經存在 BeanDefinition
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
            //是否允許重複定義 默認是 true 
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
         ...
            // 將 beanDefinition 存入 beanDefinitionMap 中
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
            //如果 Bean 已經開始創建
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {//加鎖保證操作的安全性
                    // 將 beanDefinition 存入 beanDefinitionMap 中
					this.beanDefinitionMap.put(beanName, beanDefinition);
            ...
				}
			}
			else {//正常創建
				// Still in startup registration phase
                // 將 beanDefinition 存入 beanDefinitionMap 中
				this.beanDefinitionMap.put(beanName, beanDefinition);
                //主要是爲了註冊的順序
				this.beanDefinitionNames.add(beanName);
                //刪除掉註冊的單例對象,互斥操作
				removeManualSingletonName(beanName);
			}
       ...
	}

5.Spring BeanDefinition 合併階段

BeanDefintion 合併

  • 父子 BeanDefinition 合併
    • 當前 BeanFactory 查找
    • 層次性 BeanFactory 查找

5.1 XML 配置

dependency-lookup-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--<context:component-scan base-package="com"/>-->
    <!--<context:annotation-config/>-->

    <bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
        <property name="name" value="xwf"/>
        <property name="age" value="18"/>
        <property name="id" value="1"/>
        <property name="configFileReource" value="classpath:/META-INF/user-config.properties"/>
        <property name="city" value="WUHAN"/>
        <property name="cities" value="WUHAN,BEIJING"/>
        <property name="lifeCities" value="WUHAN,BEIJING"/>
    </bean>


    <bean primary="true" id="superUser" class="com.huajie.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user">
        <property name="address" value="wuhan"/>
    </bean>

    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>

</beans>

user 是一個典型的通過 xml 方式配置的 BeanDefinition,user 沒有 parent 屬性,可以類比 user 是一個 Root BeanDefintion 不需要合併,實際情況還是一個普通的 BeanDefintion。

  • org.springframework.beans.factory.support.RootBeanDefinition

而 superUser 的 parent 屬性指向 user,表示 superUser 是一個普通的 BeanDefintion,並且需要合併 user 中的字段屬性,這樣設計的好處主要是爲了優化我們配置的方式。

  • org.springframework.beans.factory.support.GenericBeanDefinition

5.2 示例

/**
 * BeanDefinition 合併示例
 */
public class MergedBeanDefinitionDemo {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        String location = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加載 XML 資源
        int count = reader.loadBeanDefinitions(location);
        System.out.println(count);
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        Stream.of(beanDefinitionNames).forEach(System.out::println);

        User user = beanFactory.getBean("user", User.class);
        SuperUser superUser = beanFactory.getBean("superUser", SuperUser.class);

        System.out.println(user);
        System.out.println(superUser);
    }
}

執行結果:

3
user
superUser
objectFactory
User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}

我們定義了 3 個 BeanDefinition,分別是 user、superUser、objectFactory,可以看到 SuperUser 合併了 User 的屬性;通過這個示例我們來調試下源碼。

5.3 源碼分析

先分析下相關的源碼:

org.springframework.beans.factory.config.ConfigurableBeanFactory#getMergedBeanDefinition

	/**
	 * Return a merged BeanDefinition for the given bean name,
	 * merging a child bean definition with its parent if necessary.
	 * Considers bean definitions in ancestor factories as well.
	 * @param beanName the name of the bean to retrieve the merged definition for
	 * @return a (potentially merged) BeanDefinition for the given bean
	 * @throws NoSuchBeanDefinitionException if there is no bean definition with the given name
	 * @since 2.5
	 */
	BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

通過 beanName 返回一個被合併的 BeanDefinition,合併 child bean definition 和它的 parent。

這個接口也是隻有一個唯一實現

org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition(java.lang.String)

	@Override
	public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
		String beanName = transformedBeanName(name);
		// Efficiently check whether bean definition exists in this factory.
		if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
			return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
		}
		// Resolve merged bean definition locally.
		return getMergedLocalBeanDefinition(beanName);
	}

這是一個遞歸的方法,如果當前 BeanFactory 不包含這個 beanName 並且 Parent BeanFactory 是 ConfigurableBeanFactory 這個類型,那麼繼續往下查找,如果有的話在當前的 BeanFactory 中查找。

	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        // 如果 mbd 不爲空 並且 沒有過期
		if (mbd != null && !mbd.stale) {
			return mbd;
		}
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

在 mergedBeanDefinitions 這個集合中查找,這個集合是合併之後的 BeanDefinition 存放的集合,集合元素類型爲 RootBeanDefinition,這裏需要注意的是 mergedBeanDefinitions 表示的只是當前 BeanFactory 中的 BeanDefinition,如果有多層 BeanFactory ,每個 BeanFactory 都會有這個 mergedBeanDefinitions 的緩存。

第一次進來肯定是沒有的,我們繼續往下看,最終到這個方法中,參數中的 containingBd 表示的是嵌套 Bean 的情況,我們這裏不討論。

	protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {
        // 下面的操作既有 get 也有 put,需要加鎖保證安全性,而且這個方法可能在很多地方調用
		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;

			// Check with full lock now in order to enforce the same merged instance.
            // 如果爲空,表示當前的 BeanDefintion 並不是一個嵌套 Bean 而是頂層 Bean
			if (containingBd == null) {
            // 再從 mergedBeanDefinitions 獲取,這裏主要是防止有其他線程已經添加了這個 BeanDefinition
				mbd = this.mergedBeanDefinitions.get(beanName);
			}

			if (mbd == null || mbd.stale) {
				previous = mbd;
				if (bd.getParentName() == null) {
					// Use copy of given root bean definition.
					if (bd instanceof RootBeanDefinition) {
                        // 如果是 RootBeanDefinition 直接返回
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
                        // 構建一個 RootBeanDefinition
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
                    // 示例中 SuperUser 屬於這一種情況
					// Child bean definition: needs to be merged with parent.
					BeanDefinition pbd;
					try {
                        // 獲取 parent 屬性中 BeanName
						String  parentBeanName = transformedBeanName(bd.getParentName());
						// 如果 beanName和parentBeanName不相同
                        if (!beanName.equals(parentBeanName)) {
                            // 獲取 parent 的合併之後的 BeanDefintion,因爲 parent 有可能也是一個被合併的 BeanDefintion
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {// 如果相同的話,去 parent BeanFactory 中做層次性的查找
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
               ...
					// Deep copy with overridden values.
					mbd = new RootBeanDefinition(pbd);
					mbd.overrideFrom(bd);
				}
               ...
				if (containingBd == null && isCacheBeanMetadata()) {
                    // 將合併之後的 mbd 存放到 mergedBeanDefinitions 集合中
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			return mbd;
		}
	}

5.4 源碼調試

局部變量命名說明

  • bd -> BeanDefinition

  • mdb -> MergedBeanDefinition 被合併(Merged)之後的 BeanDefinition

斷點打在 AbstractBeanFactory#getMergedBeanDefinition() 1309 行

第一次 user 進來 mergedBeanDefinitions 集合爲空,此時參數 beanName = user
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KhCNtov8-1588318851632)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428094109055.png)]
mbd 必然也是爲 null,而且 user 沒有 parent 屬性,所以 bd.getParentName() == null 成立,bd 目前還是普通的 BeanDefinition,
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-X4PTy1Mq-1588318851634)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428094325736.png)]
合併完成,將 mdb 存入 mergedBeanDefinitions 集合,最後返回 mdb 對象。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VpuPTnCA-1588318851636)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428094705213.png)]
第二次 superUser 進來,此時 mergedBeanDefinitions 集合中已經存在了user的 RootBeanDefintion 對象。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-YWXCXtEQ-1588318851637)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428095029864.png)]
區別在於這次 bd.getParentName() == null 不成立,所以進入下面的邏輯
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0KnlTZbs-1588318851637)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428095137971.png)]
最終獲取到 superUser 的 parent BeanDefintion(也就是 user 的 BeanDefintion)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CkI7KkSV-1588318851638)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428095618125.png)]
通過 mbd.overrideFrom(bd) 進行合併操作
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-t8vCorxg-1588318851639)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428095436266.png)]
再將 superUser 存入到 mergedBeanDefinitions 並返回 superUser 合併之後的 mdb 對象。

5.5 結論

通過上面的調試過程和源碼分析可知

  • user 和 superUser 都會經過合併的過程,而且最後都變成了 RootBeanDefintion
  • superUser 合併了 user 的屬性

6.Spring Bean Class 加載階段

ClassLoader 類加載

Java Security 安全控制

ConfigurableBeanFactory 臨時 ClassLoader

6.1 源碼調試

還是用上面的例子進行,我們將斷點打在

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 249 行

第一次 user 進來
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nZurHXgJ-1588318851640)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428111408548.png)]
1.中間的一些過程跳過,比如 getSingleton(beanName) 因爲我們是通過 XML 的方式配置的 BeanDefinition,並不是通過 registerSingleton 這種方式,所以這一段跳過

2.user 沒有 parentBeanFactory,這一段也跳過

3.dependsOn 這種配置方式我們也沒有采用,繼續跳過

4.我們將斷點打在 320 行,因爲 BeanDefintion 默認是 singleton 作用域,這個 mbd.isSingleton() 判斷成立
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-B1EGaltu-1588318851641)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428112036099.png)]
createBean 方法中 resolveBeanClass 方法就是類加載過程

	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
        ...
        // 返回user的Class,之前是字符串類型,處理完之後返回 Class 類型
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
	    ...
	}

AbstractBeanFactory#doResolveBeanClass() 1508 行
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fOUcwwlQ-1588318851641)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428114356501.png)]
可以到看到 mbd.getBeanClassName() 的屬性的類型是 String 類型,這裏還不是真正的 Class。

AbstractBeanDefinition#resolveBeanClass 通過 forName 加載這個 Bean 的 Class,得到這個 resolvedClass 的 Class 並返回。

	public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {
		String className = getBeanClassName();
		if (className == null) {
			return null;
		}
		Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
		this.beanClass = resolvedClass;
		return resolvedClass;
	}

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3m8MNUgx-1588318851642)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200428115158649.png)]

6.2 結論

從源碼分析可以看出,其實 Spring BeanDefinition 變成 Class 的過程其實還是通過傳統 Java 的 ClassLoader 來進行加載的。

7.參考

  • 極客時間-小馬哥《小馬哥講Spring核心編程思想》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章