Spring @Value的注解使用和原理解析
介紹
@Value注解在Spring開發(fā)中是一個使用很頻繁的注解,在項目開發(fā)中,我們通常需要讀取配置文件中的一些信息,對于SpringBoot項目,我們一般從yml文件中讀取,如果我們自定義了配置文件,那么就可以配合@PropertySource注解來獲取配置文件的配置項,當(dāng)然,@Value不單單能讀取配置文件,還能讀取系統(tǒng)屬性,還可以讀取其他bean的屬性,本章就來詳細介紹@Value注解的使用和對源碼進行分析。
使用
如下我們對value的使用進行詳細介紹,value可以注入配置文件的屬性,注入其它bean的屬性,注冊spring中自己實現(xiàn)的一些屬性,比如操作系統(tǒng)信息。
屬性類
MyProperties是一個bean,里面定義了一些屬性,一般在項目中,如果需要全局使用某個配置信息,我們通常會定義一個屬性類,然后在需要使用的地方直接注入,比如系統(tǒng)中我們需要存儲大量的文件,文件是存儲在文件服務(wù)器上面,數(shù)據(jù)庫只存儲文件所在文件系統(tǒng)的目錄路徑,而不會存儲具體的ip地址,如果我們存儲了能直接訪問文件的鏈接,后續(xù)如果進行文件遷移,那么這些鏈接就不好處理,所以應(yīng)該只存儲文件在文件服務(wù)器的目錄路徑,那么返回給前端顯示的時候,再獲取文件服務(wù)器地址進行拼接就可以。
/** * 功能說明: 屬性配置類 * <p> * Original @Author: steakliu-劉牌, 2023-04-27 10:08 * <p> * Copyright (C)2020-2022 steakliu All rights reserved. */ @Data @Component public class MyProperties { /** * 注入其他bean的屬性 */ @Value("#{valueBean.username}") private String username; /** * 注入配置文件屬性 */ @Value("${minio.url}") private String minioUrl; /** * 注入操作系統(tǒng)屬性 */ @Value("#{systemProperties['os.name']}") private String os; }
配置類
配置類主要就是使用@PropertySource
注解來獲取配置配置文件的屬性。
@Configuration @PropertySource("classpath:minio.properties") public class ValueConfiguration { }
配置文件minio.properties
配置文件里面就放了一個minio的地址
minio.url=http://www.gss.cn/
通過上面的配置,我們就可以在需要使用minio地址的地方注入MyProperties這bean就可以,可能有些人會覺得麻煩,還需要注入bean,直接寫在一個常量里面不就行,其實不然,這樣做更加的規(guī)范,做到了配置和代碼的分離,不同的環(huán)境的地址不同,或者發(fā)生文件遷移,就可以直接修改配置文件,還有配置文件可以寫入注冊中心,可以更具一定的策略進行修改后刷新,@Value注解只是獲取配置文件屬性的一種方式,在SpringBoot中,@ConfigurationProperties
使用起來也很方便。
原理解析
下面對@Value的原理進行解析,因為我們使用@Value大多時候是放在字段上面,并且要使用在一個Bean中,那么我們知道bean在實例化的時候需要進行屬性填充,就會對這些屬性進行賦值,所以下面就從實例化bean開始對@Value進行解析。
解析屬性
我們從AbstractAutowireCapableBeanFactory
類這里開始,在類中進入doCreateBean()
方法,然后進入applyMergedBeanDefinitionPostProcessors
,最終會進入AutowiredAnnotationBeanPostProcessor
后置處理器中,@Autowired,@Value,@Inject都是它進行處理,下面我們看最主要的部分buildAutowiringMetadata
。
如下代碼,Spring使用反射獲取字段,如果是字段被static修飾,那么在此處是會被排除,使用的是Modifier.isStatic(int mod)
方法,通過反射拿到字段后,組裝后加入一個名字為injectionMetadataCache
的Map中,后面屬性填充會直接從這個緩存中獲取。
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) { List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; do { final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required)); } }); } while (targetClass != null && targetClass != Object.class); return InjectionMetadata.forElements(elements, clazz); }
屬性填充
屬性填充階段進入的是對bean的屬性進行賦值,這是Spring生命周期中很重要的一個階段,方法是populateBean
,也在AbstractAutowireCapableBeanFactory
類中,接著會調(diào)用AutowiredAnnotationBeanPostProcessor
中的postProcessProperties
方法,然后往下繼續(xù)執(zhí)行,核心代碼如下,如下就是給每個屬性賦值,往下執(zhí)行還有很多邏輯處理,如解析@Value
注解的表達式,然后根據(jù)表達式去獲取對應(yīng)的值等,就不深入去解析。
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { try { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // Unexpected removal of target bean for cached argument -> re-resolve value = resolveFieldValue(field, bean, beanName); } } else { value = resolveFieldValue(field, bean, beanName); } if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } }
總結(jié)
上面對@Value的使用和原理進行了介紹,其實@Value,@Autowired,@Resource,@Inject這幾個的作用都是進行屬性裝配,只不過他們的方式各有不同,@Value,@Autowired,@Inject是使用AutowiredAnnotationBeanPostProcessor
后置處理器進行處理,@Resource則使用CommonAnnotationBeanPostProcessor
后置處理器進行處理。
以上就是Spring @Value的注解使用和原理解析的詳細內(nèi)容,更多關(guān)于Spring @Value注解的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot項目運行一段時間后自動關(guān)閉的坑及解決
這篇文章主要介紹了SpringBoot項目運行一段時間后自動關(guān)閉的坑及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09SpringBoot 整合 Shiro 密碼登錄的實現(xiàn)代碼
這篇文章主要介紹了SpringBoot 整合 Shiro 密碼登錄的實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02在eclipse導(dǎo)入Java的jar包的方法JDBC(圖文說明)
這篇文章主要介紹了在eclipse導(dǎo)入Java 的jar包的方法 JDBC 圖文說明 ,需要的朋友可以參考下2015-09-09Jmeter中的timeshift()函數(shù)獲取當(dāng)前時間進行加減
這篇文章主要介紹了Jmeter中的timeshift()函數(shù)獲取當(dāng)前時間進行加減,TimeShift(格式,日期,移位,語言環(huán)境,變量)可對日期進行移位加減操作,本文給大家詳細講解,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10mybatis關(guān)聯(lián)關(guān)系映射的實現(xiàn)
MyBatis的關(guān)聯(lián)關(guān)系映射在復(fù)雜數(shù)據(jù)模型中至關(guān)重要,使開發(fā)人員能夠以最靈活的方式滿足不同項目的需求,本文就來介紹一下mybatis關(guān)聯(lián)關(guān)系映射的實現(xiàn),感興趣的可以了解一下2023-09-09