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

關(guān)于Spring中@Value注解使用和源碼分析

 更新時(shí)間:2024年11月05日 14:45:48   作者:魔道不誤砍柴功  
通過深入分析@Value注解的使用和源碼,本文詳細(xì)解釋了Spring如何解析@Value注解并為屬性賦值,首先,Spring會(huì)解析并收集所有被@Value注解修飾的屬性,這一過程依賴于AutowiredAnnotationBeanPostProcessor類

1、@Value 注解使用

先配置本地 application.properties 如下:

apple.name=abc

代碼如下:

@PropertySource("application.properties")
public class Apple {
	@Value("${apple.name}")
	public String name;
}

@ComponentScan
public class AtValueTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AtValueTest.class);
		Apple bean = context.getBean(Apple.class);
		System.out.println("bean.name = " + bean.name);
	}
}

結(jié)果如下:

bean.name = abc

可以看到最終在 Apple 中可以獲取到配置文件中的值,那么 Spring 是怎樣解析獲取到該值的呢?下面開始分析下。

2、@Value 注解源碼分析

分析源碼之前,我么可以簡單思考下, Spring 會(huì)怎么樣去處理這個(gè) @Value 注解的?然后再帶著我們的問題去看看 Spring 是不是和我們想的一樣呢?

簡單分析有三個(gè)步驟,如下:

  1. 首先 Spring 肯定要去解析收集該注解,找到被 @Value 注解修飾的所有屬性,注意此時(shí)屬性的值是一個(gè)占位符 ${apple.name},并不是真正的值
  2. 然后加載一個(gè)資源文件,可以是本地 application.properties、也可以是其他,比如 Environment 、xml
  3. 最后去判斷哪個(gè)類上的屬性有 @Value 修飾,就把占位符替換成資源文件中配置的值

在腦海中有了大致思路,再去追蹤源碼就事半功倍了。

2.1、Spring 解析并收集 @Value 修飾的屬性

Spring 解析收集 @Value 修飾的屬性和解析收集 @Autowired 注解一模一樣,懂這個(gè),@Autowired 注解解析流程也就懂了。

Spring 提供很多 BeanFactoryPostProcessor、BeanPostProcessor 接口作為擴(kuò)展,從而使 Spring 非常強(qiáng)大,因?yàn)槲覀儗傩再x值相當(dāng)于是在實(shí)例化之后的事,所以這里的 @Value 解析就是 BeanPostProcessor 接口的應(yīng)用啦!

一個(gè)大家非常熟悉的類 AutowiredAnnotationBeanPostProcessor,Spring 的 DI(依賴注入) 就是這個(gè) BeanPostProcessor 完成的,接下來看 Spring 源碼,如下:

可以看到 Spring 是在實(shí)例化之后開始去收集的,這個(gè)時(shí)序非常重要,一定要注意

然后進(jìn)入 AutowiredAnnotationBeanPostProcessor 類核心部分,如下:

對(duì)過來的每個(gè)類進(jìn)行篩選判斷是否有被 @Value、@Autowired 修飾的方法或者屬性,如果找到有,就會(huì)將這個(gè)類記錄下來,放到一個(gè) injectionMetadataCache 緩存中,為后續(xù)的 DI 依賴注作準(zhǔn)備,注意哦,解析并收集到的結(jié)果最終放到 Spring 的 injectionMetadataCache 緩存中

進(jìn)入 buildAutowiringMetadata() 方法內(nèi)部,如下:

獲取到這個(gè)類上所有的屬性,然后遍歷每個(gè)屬性,判斷是否有 @Value、@Autowired 修飾,如果有,直接封裝成 AutowiredFieldElement 對(duì)象,然后保存到一個(gè)名為 currElements List 容器中

最后在封裝到 InjectionMetadata 對(duì)象中,最終返回出去放到 injectionMetadataCache 緩存中保存,不用管他封裝到哪個(gè)對(duì)象,反正這里就是掃描并解析到了哪些屬性,或者方法后續(xù)需要做處理就可以了。

上面源碼中 findAutowiredAnnotation() 方法內(nèi)部邏輯如下:

AutowiredAnnotationBeanPostProcessor 類創(chuàng)建的時(shí)候,Spring 就默認(rèn)往 autowiredAnnotationTypes 容器中添加兩個(gè)元素,如下:

至此,解析收集 @Value 修飾的屬性已經(jīng)完成,最終將收集到的結(jié)果放到了 injectionMetadataCache 緩存中保存,后續(xù)需要使用直接可以從這個(gè)緩存中獲取即可。

2.2、Spring 為 @Value 修飾屬性賦值

上面通過 postProcessMergedBeanDefinition() 方法收集好了 @Value 注解修飾的屬性,那么下面要做的就是去為這個(gè)屬性進(jìn)行賦值。

進(jìn)入屬性填充的方法,源碼如下:

然后進(jìn)入到 AutowiredAnnotationBeanPostProcessor 類中,源碼如下:

注意此時(shí)的 injectionMetadataCache 緩存中早已經(jīng)有值了,因?yàn)榍懊嫖覀兙鸵呀?jīng)收集完成了 @Value 修飾的屬性,所以這里直接從緩存中就可以獲取到。

然后進(jìn)入 metadata.inject(bean, beanName, pvs) 代碼內(nèi)部,如下:

最終通過 resolveFieldValue() 方法獲取到屬性值,然后通過反射 field.set() 方法給這個(gè)屬性賦值,如下:

至此,整個(gè) @Value 的流程就算完成,下面就是對(duì)這個(gè) resolveFieldValue() 方法進(jìn)一步分析,看下是怎么獲取到屬性值的,是怎么樣將 $ 符號(hào)替換成解析成真正的值的

2.3、Spring $ 占位符替換成真正的值

繼續(xù)深入分析 resolveFieldValue() 方法,核心源碼如下:

下面這段邏輯是去解析 ${apple.name} 占位符的,這里面為什么需要遞歸 parseStringValue(),因?yàn)榕履愠霈F(xiàn)這種形式的占位符 ${ ${apple.name} },最終獲取到 key = apple.name,然后拿著這個(gè) key 就去資源文件(xml、application.properties、Environment 等)中查找是否配置了這個(gè) key 的值

注意這里是函數(shù)式寫法,傳入一個(gè)方法體 this::getPropertyAsRawString,后面會(huì)回調(diào)到這里。


當(dāng)調(diào)用 resolvePlaceholder() 方法時(shí),回調(diào)到 getPropertyAsRawString() 方法,源碼如下:

可以看到最終會(huì)調(diào)用到 getProperty() 方法獲取到對(duì)應(yīng) key = apple.name 的值

propertySources 資源中獲取 key = apple.name 的值,只要在這里獲取到一個(gè)值就直接 return 出去即可

propertySources 這里會(huì)有三個(gè),如下所示:

PropertiesPropertySource:封裝操作系統(tǒng)屬性鍵值對(duì)SystemEnvironmentPropertySource:封裝 JVM Environment 里面的鍵值對(duì)ResourcePropertySource:封裝 application.properties、xml 中鍵值對(duì)

然后 debug 發(fā)現(xiàn)最終是從 ResourcePropertySource 資源對(duì)象中獲取到 apple.name 對(duì)應(yīng)的值 abc,最終將 ${apple.name} 替換成真正的值 abc,最終通過反射將該值 abc 賦值到 Apple 類中的 name 屬性上。

那么這里肯定有很多人會(huì)有這樣的疑問?這三個(gè)對(duì)象是從哪里來的呢?如果要想知道這個(gè),需要下面一些知道做鋪墊,那么繼續(xù)往下看 !

2.4、理解 PropertySource 和 MutablePropertySource

在 Spring 中需要加載一些額外的配置文件,比如操作系統(tǒng)相關(guān)的配置,JVM 環(huán)境變量相關(guān)的配置,自定義配置文件等等。那么這些文件加載到代碼中可定要有一個(gè)類來封裝它,這個(gè)類就是 PropertySource,先來看看 PropertySource 的源碼如下:

public abstract class PropertySource<T> {

	protected final Log logger = LogFactory.getLog(getClass());
	
	// 給這個(gè)配置文件起個(gè)名字唄
	protected final String name;

    // 配置文件中所有的 key-value 鍵值對(duì) 
    // T 只要是 key-value 鍵值對(duì)即可,比如: Map、Properties 都可以
	protected final T source;

	public String getName() {
		return this.name;
	}

	public T getSource() {
		return this.source;
	}

    // 根據(jù) name 獲取到對(duì)應(yīng)的配置文件
	@Nullable
	public abstract Object getProperty(String name);
}

看完這個(gè) PropertySource 類的結(jié)構(gòu),我們看看上面三個(gè)類中封裝的屬性到底是啥?如下所示:

  • PropertiesPropertySource:封裝操作系統(tǒng)屬性鍵值對(duì)(os.name、file.encoding、user.name 等等)

  • SystemEnvironmentPropertySource:封裝 JVM Environment 里面的鍵值對(duì)(PATH、JAVA_HOME)

  • ResourcePropertySource:封裝 application.properties、xml 中鍵值對(duì)

理解了 PropertySource 這個(gè)類之后,在來理解 MutablePropertySource 就非常容易。

先來看看 MutablePropertySource 的源碼,如下:

public class MutablePropertySources {

	private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();

}

從源碼中可以看到,就是做了一個(gè)收集,將所有的 PropertySource 收集到一個(gè) propertySourceList 容器中進(jìn)行管理,至于為什么這樣做呢?

個(gè)人理解是為了更方便的查找屬性,將所有的資源文件匯集到一起,然后想要找一個(gè) key = apple.name 就可以直接遍歷一下所有的資源文件,看下這個(gè) key 在哪個(gè)資源文件中能夠找到,找到立即返回。

但是此時(shí)就會(huì)存在一個(gè)問題,那就是文件的加載順序,可以發(fā)現(xiàn)最先加載 PropertiesPropertySource、其次 SystemEnvironmentPropertySource,最后才是自定義的配置文件 ResourcePropertySource!比如,我們?cè)?application.properties 中配置 user.home=abc 如下所示:

user.home=abc
@Component
@PropertySource("application.properties")
public class Apple {
	@Value("${user.home}")
	public String name;
}

輸出結(jié)果:

name = /Users/gongwm

并不是 abc,因?yàn)?PropertiesPropertySource 優(yōu)先于 ResourcePropertySource 被加載。

下面這個(gè)源碼是獲取資源文件,可以發(fā)現(xiàn)只要獲取到就會(huì)立即 return,所以最終決定權(quán)交給了往 propertySources 容器添加的順序決定,那么我們來看看上面三個(gè)文件分別是在什么時(shí)候方進(jìn)去的?

2.5、Spring 資源文件裝載源碼分析?

直接進(jìn)入源碼分析,如下:


然后再 StandardEnvironment 的構(gòu)造方法中,隱式調(diào)用父類 AbstractEnvironment 的構(gòu)造方法,源碼如下:

可以發(fā)現(xiàn)在這里直接 new 創(chuàng)建了 MutablePropertySources 對(duì)象

最終可以發(fā)現(xiàn)這兩個(gè) systemProperties、systemEnvironment 資源文件都是在這里被加載的,添加到了 MutablePropertySources 對(duì)象中

對(duì)于自定義的配置文件在 ConfigurationClassPostProcessor 類中被加載,源碼如下:

最終就是在這里被加載進(jìn)去的,注意這個(gè)添加方法是 addLast() 也就是往后面追加,這個(gè)方法就體現(xiàn)了這些資源文件的加載順序,那么有 addLast() ,必然有 addFirst() 等等 API,此時(shí) propertySourceList 容器中就已經(jīng)保存了三個(gè)資源文件,并且順序是這樣的PropertiesPropertySource(優(yōu)先級(jí)最高) -> SystemEnvironmentPropertySource -> ResourcePropertySource (優(yōu)先級(jí)最低)

最后有一點(diǎn)要注意,不要把 MutablePropertySourcesMutablePropertyValues 搞混了,兩完全不是一一碼事!具體想看 MutablePropertyValues 是啥,可以轉(zhuǎn)到另一篇文章!

2.6、在 Environment 中添加自定義屬性

借助 BeanDefinitionRegistryPostProcessor 類來實(shí)現(xiàn)這個(gè)功能,如下所示:

@Component
public class AppendAttrToEnvironment implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware {

	private ResourceLoader resourceLoader;

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		// 從容器中獲取到 Environment 對(duì)象
		StandardEnvironment bean = (StandardEnvironment)beanFactory.getBean(Environment.class);
		// 然后再獲取到 MutablePropertySources 資源文件管家(它會(huì)收集打包所有的資源文件)
		MutablePropertySources propertySources = bean.getPropertySources();

		// 創(chuàng)建第一個(gè)資源文件,其中有個(gè)屬性 key666 值為 hangman
		Properties properties = new Properties();
		properties.put("key666", "hangman");
		PropertiesPropertySource propertiesSource = new PropertiesPropertySource("myPropertySource",properties);
		// 添加到 MutablePropertySources 資源文件管家中,注意使用 addLast() 添加的,也就是往后追加
        propertySources.addLast(propertiesSource);


		// 創(chuàng)建第二資源文件,看下面的 abc.properties 配置文件
		Resource resource = resourceLoader.getResource("abc.properties");
		ResourcePropertySource localResource = new ResourcePropertySource("myResourceSource",resource);
		// 添加到 MutablePropertySources 資源文件管家中,注意是添加到最前面了,這樣就會(huì)被最先加載
		propertySources.addFirst(localResource);
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}
}

創(chuàng)建一個(gè) abc.properties 配置文件如下所示:

key666 = 6666key777 = ggg

然后在一個(gè) Apple 類中去獲取對(duì)應(yīng)的值,如下所示:

@Component
@PropertySource("application.properties")
public class Apple implements EnvironmentAware {
	@Value("${apple.name}")
	public String name;

	@Override
	public void setEnvironment(Environment environment) {
		String property = environment.getProperty("apple.name");
		String key666 = environment.getProperty("key666");
		String key777 = environment.getProperty("key777");
		System.out.println("key666 = " + key666);
		System.out.println("key777 = " + key777);
		System.out.println("name = " + name);
		System.out.println("property = " + property);
	}
}

輸出結(jié)果如下:

key666 = 6666
key777 = ggg
name = abc
property = abc
bean.name = abc

這里會(huì)發(fā)現(xiàn)一個(gè)問題,key = key666 的這個(gè)鍵值對(duì),在 propertiesSource 資源和 localResource 資源文件同時(shí)出現(xiàn),最終因?yàn)?localResource 資源是通過 addFirst() 方法添加到 MutablePropertySources 管家容器最前面,優(yōu)先生效。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決因缺少Log4j依賴導(dǎo)致應(yīng)用啟動(dòng)失敗的問題

    解決因缺少Log4j依賴導(dǎo)致應(yīng)用啟動(dòng)失敗的問題

    日志是應(yīng)用軟件中不可缺少的部分,Apache的開源項(xiàng)目log4j是一個(gè)功能強(qiáng)大的日志組件,提供方便的日志記錄。但這篇文章不是介紹Log4j,這篇文章主要介紹了關(guān)于因缺少Log4j依賴導(dǎo)致應(yīng)用啟動(dòng)失敗問題的相關(guān)資料,需要的朋友可以參考下。
    2017-04-04
  • java實(shí)現(xiàn)簡單美女拼圖游戲

    java實(shí)現(xiàn)簡單美女拼圖游戲

    這篇文章主要介紹了java實(shí)現(xiàn)簡單美女拼圖游戲的相關(guān)資料,需要的朋友可以參考下
    2015-03-03
  • Spring的@Lazy懶加載注解用法詳細(xì)解析

    Spring的@Lazy懶加載注解用法詳細(xì)解析

    這篇文章主要介紹了Spring的@Lazy懶加載注解用法詳細(xì)解析,SpringIoC容器會(huì)在啟動(dòng)的時(shí)候?qū)嵗袉螌?shí)例 bean ,如果我們想要實(shí)現(xiàn) Spring 在啟動(dòng)的時(shí)候延遲加載 bean,即在首次調(diào)用bean的時(shí)候再去執(zhí)行初始化,就可以使用 @Lazy 注解來解決這個(gè)問題,需要的朋友可以參考下
    2023-11-11
  • JSP頁面?zhèn)鲄⒊霈F(xiàn)中文亂碼的解決方案

    JSP頁面?zhèn)鲄⒊霈F(xiàn)中文亂碼的解決方案

    這篇文章主要介紹了JSP頁面?zhèn)鲄⒊霈F(xiàn)中文亂碼的解決方案,非常實(shí)用,需要的朋友可以參考下
    2014-08-08
  • Java計(jì)算器核心算法代碼實(shí)現(xiàn)

    Java計(jì)算器核心算法代碼實(shí)現(xiàn)

    今天小編就為大家分享一篇關(guān)于Java計(jì)算器核心算法代碼實(shí)現(xiàn),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Java Swing界面美化與JPanel優(yōu)化的完全指南

    Java Swing界面美化與JPanel優(yōu)化的完全指南

    ava Swing 作為經(jīng)典的桌面應(yīng)用框架,功能強(qiáng)大但界面設(shè)計(jì)往往停留在上個(gè)時(shí)代,本文系統(tǒng)介紹如何使用 FlatLaf 等現(xiàn)代化工具美化 Swing 界面,并提供 JPanel 布局優(yōu)化的完整解決方案,讓你的桌面應(yīng)用煥然一新,需要的朋友可以參考下
    2026-02-02
  • Java函數(shù)式編程(七):MapReduce

    Java函數(shù)式編程(七):MapReduce

    這篇文章主要介紹了Java函數(shù)式編程(七):MapReduce,本文是系列文章的第7篇,其它文章請(qǐng)參閱本文底部的相關(guān)文章,需要的朋友可以參考下
    2014-09-09
  • Spring JPA事務(wù)管理與自定義操作實(shí)例解析(最新推薦)

    Spring JPA事務(wù)管理與自定義操作實(shí)例解析(最新推薦)

    在Spring框架中,數(shù)據(jù)持久化操作常常與事務(wù)管理緊密相關(guān),本文將深入探討Spring Data JPA中的事務(wù)管理機(jī)制,并結(jié)合具體實(shí)例,展示如何自定義事務(wù)行為以滿足不同的業(yè)務(wù)需求,感興趣的朋友一起看看吧
    2024-12-12
  • 了解SpringMVC的上傳和下載

    了解SpringMVC的上傳和下載

    今天小編就為大家分享一篇關(guān)于Spring整合Springmvc的相關(guān)介紹,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2021-07-07
  • @Async異步線程池以及線程的命名方式

    @Async異步線程池以及線程的命名方式

    這篇文章主要介紹了@Async異步線程池以及線程的命名方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06

最新評(píng)論

内丘县| 建昌县| 通榆县| 临沧市| 长兴县| 惠安县| 元江| 九江市| 汉川市| 额敏县| 明星| 武安市| 古丈县| 东莞市| 浦北县| 长宁县| 五指山市| 宜宾市| 定西市| 临夏县| 广州市| 鄂托克前旗| 临邑县| 临江市| 塔城市| 永春县| 林周县| 临武县| 恩施市| 宁海县| 杂多县| 宁乡县| 嫩江县| 岐山县| 济阳县| 汽车| 红河县| 洛阳市| 双桥区| 临高县| 长武县|