springboot @Value實現(xiàn)獲取計算機中絕對路徑文件的內(nèi)容
springboot @Value獲取絕對路徑文件的內(nèi)容
默認(rèn)情況下使用
@Value("aaa.txt") private Resource txtResource;
這樣獲取到的是項目classpath 下的 aaa.txt
如果想獲取非項目路徑下的文件內(nèi)容怎么辦呢,看了下@Value的好像也沒有說,
其實
@Value("https://www.baidu.com") private Resource urlResource;
這樣是可以獲取到 百度首頁的內(nèi)容的.它這里使用的是https協(xié)議.
同樣的我們可以使用file協(xié)議獲取文本的內(nèi)容
即:
@Value("file:///E://aaa.txt") private Resource txtResource;
使用@Value 有一個好處就是,你不用關(guān)心文本內(nèi)容的變化,你每次調(diào)用的時候,springboot 會自動幫你重新加載.
Spring注解@Value解讀
主要通過源碼解讀來分析@Value實現(xiàn)屬性注入Spring Bean的過程,并對static類型字段無法通過@Value注入為Spring Bean依賴的原因做一個探究。
依賴注入概述
基于Spring MVC或者Spring Boot開發(fā)后端項目的時候總是繞不開Spring IOC容器,Spring IOC容器管理Spring Bean,我們可以通過XML或者注解的方式來定義一個Bean,如通過注解@Service,@Controller,@Component,@Repository或者@Bean加Java配置方式。實際應(yīng)用中我們定義的一個Bean很多時候存在互相之間的依賴,比如Service層通過@Service定義的Bean往往要依賴數(shù)據(jù)庫DAO層通過@Repository定義的Bean,這時候我們往往通過@Autowired或@Resource來自動裝配,另外還有一些Bean內(nèi)部的屬性(Field)需要通過配置文件中定義的值來設(shè)置,而后創(chuàng)建的Bean才是符合我們預(yù)期的,我們一般通過@Value和@ConfigurationProperties來實現(xiàn)屬性的注入。
實際應(yīng)用案例
以文檔管理服務(wù)為例,我們有個資源上傳的接口,其依賴一個FeignClient客戶端Bean,一個業(yè)務(wù)邏輯處理的Service Bean,還依賴一個資源上傳的OSS的桶bucket成員變量,bucket和OSS我們通常配置在配置文件中以區(qū)分不同環(huán)境。具體的依賴關(guān)系如下圖:
源碼解讀分析
首先看一下Spring Bean的的整個生命周期:
在實例化對象完成后,設(shè)置屬性值(polulateBean)之前,會搜集類上的注解元數(shù)據(jù)信息,然后在polulalteBean中攔截,執(zhí)行BeanPostProcessor中的方法,反射注入依賴的值。@Resource是jdk提供的注解,其使用的后置處理器是CommonAnnotationBeanPostProcessor,而@Autowired和@Value注解使用的后置處理器是AutowiredAnnotationBeanPostProcessor,具體的我們下面會通過代碼來解讀。
先來看一下BeanFactory和ApplicationContext在裝載bean時候的區(qū)別:
- BeanFactory在啟動時不會去實例化Bean,當(dāng)從容器中拿Bean的時候才會去實例化;
- ApplicationContext在啟動時就把所有的Bean全部實例化了??梢詾锽ean配置lazy-init=true來讓Bean延遲實例化。
我們下面以Spring Boot和ApplicationContext來走讀一下源碼:
1、首先進(jìn)入SpringApplication.run(XXXApplication.class, args)
然后一直進(jìn)入到SpringApplication的refreshContext()方法,refreshContext()是IOC容器初始化的核心方法,完成容器上下文的刷新,主要包括各種處理器、監(jiān)聽器以及Bean的初始化等工作。
2、然后進(jìn)入到AbstractApplicationContext的refresh()方法
中的finishBeanFactoryInitialization(beanFactory)方法,這個方法就是ApplicationContext完成Spring Bean實例化和初始化的關(guān)鍵方法。
3、而后進(jìn)入到DefaultListableBeanFactory的preInstantiateSingletons()方法
此方法就是獲取到所有的beanName,然后循環(huán)的去實例化+初始化bean,而實例化+初始化的方式就是調(diào)用其getBean(String name)方法,因此我們直接調(diào)到getBean(name) ,此方法的最終實現(xiàn)是在AbstractBeanFactory中,我們進(jìn)入AbstractBeanFactory的getBean(beanName)查看,然后根據(jù)調(diào)用棧直接進(jìn)入到AbstractBeanFactory的doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly)方法中。這個方法代碼較多,我們直接查看我們要關(guān)注的核心代碼段如下:
4、然后我們進(jìn)入到AbstractAutowireCapableBeanFactory
createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法中,其中AbstractAutowireCapableBeanFactory是AbstractBeanFactory的一個子類,AbstractBeanFactory的createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)由AbstractAutowireCapableBeanFactory實現(xiàn)。createBean方法的核心是調(diào)用了AbstractAutowireCapableBeanFactory的doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args),我們直接查看doCreateBean方法。
5、我們略過創(chuàng)建實例的過程
直接進(jìn)入applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)方法。
5.1、applyMergedBeanDefinitionPostProcessors方法
主要是循環(huán)所有后置處理器對beanDefinition做處理,我們之前說過@Value注解是通過AutowiredAnnotationBeanPostProcessor來處理的,我們直接進(jìn)入AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)方法如下:
其中的InjectionMetadata如下,它主要封裝了bean的全限定類名和這個類中所有帶有注解的成員屬性(字段或者其他bean)描述(PropertyDescriptor,可以簡單理解為帶注解的成員屬性和注解本身兩者的組合),當(dāng)然也就包括由@Value注解的字段。
5.2、我們繼續(xù)進(jìn)入到
findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)方法,通過buildAutowiringMetadata(final Class<?> clazz)首先獲取了當(dāng)前bean所屬類的屬性注解元數(shù)據(jù),然后存儲到了injectionMetadataCache緩存中,完成了applyMergedBeanDefinitionPostProcessors方法。
5.3、我們進(jìn)入到
buildAutowiringMetadata(final Class<?> clazz)查看構(gòu)建bean注入注解元數(shù)據(jù)的過程
其中用到了獲取當(dāng)前成員屬性AnnotationAttributes的方法findAutowiredAnnotation(AccessibleObject ao)如下:
其中autowiredAnnotationTypes表示后置處理器AutowiredAnnotationBeanPostProcessor處理的注解列表,初始化如下:
6、第5步完成了AbstractAutowireCapableBeanFactory
doCreateBean方法中的applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName),即完成了bean的成員屬性注解元數(shù)據(jù)的本地緩存??梢岳斫鈇pplyMergedBeanDefinitionPostProcessors為一個攔截器,即在對bean進(jìn)行成員屬性和依賴注入前先預(yù)處理bean的依賴和成員屬性數(shù)據(jù)的解析和緩存,而后再進(jìn)行populateBean。
6.1、進(jìn)入到AbstractAutowireCapableBeanFactory
populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)方法,我們直接看核心代碼如下,其中postProcessProperties方法就是完成了bean的成員屬性的初始化和依賴的注入過程。
6.2、因為我們要處理的是@Value和@Autowired注解的bean依賴
AutowiredAnnotationBeanPostProcessor是InstantiationAwareBeanPostProcessor接口的實現(xiàn)類,所以上面代碼端循環(huán)過程中會進(jìn)入到之前的AutowiredAnnotationBeanPostProcessor的postProcessProperties(PropertyValues pvs, Object bean, String beanName)。
6.3、上面findAutowiringMetadata方法就是第5.2步的方法
也就是從緩存Map中取出bean對應(yīng)的注解元數(shù)據(jù),而后調(diào)用InjectionMetadata的inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)方法執(zhí)行依賴注入過程。
6.4、在上面對注解元數(shù)據(jù)列表循環(huán)處理(bean依賴了多個其他的bean或者多個成員屬性通過配置注入)進(jìn)行依賴注入
調(diào)用element.inject(target, beanName, pvs)方法。而AutowiredAnnotationBeanPostProcessor類的內(nèi)部類AutowiredFieldElement繼承了InjectionMetadata.InjectedElement類,并重寫了其inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法,AutowiredAnnotationBeanPostProcessor類的內(nèi)部類AutowiredMethodElement也繼承了InjectionMetadata.InjectedElement類,并重寫了其inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法,因此6.3中的inject調(diào)用將會在此子類中執(zhí)行,AutowiredFieldElement處理成員屬性上注解,AutowiredMethodElement處理方法上注解。因此@Value注解的處理將會進(jìn)入到AutowiredFieldElement的inject方法。
6.5、首先通過beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)方法解析依賴
然后注冊當(dāng)前bean依賴的其他bean,最后通過反射將依賴注入到當(dāng)前bean中完成bean的依賴注入過程。DefaultListableBeanFactory繼承了AbstractAutowireCapableBeanFactory類,我們先查看DefaultListableBeanFactory的resolveDependency方法,其中主要是調(diào)用了DefaultListableBeanFactory的doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter)方法,我們直接看doResolveDependency方法。
6.6、以上解析@Value注解
getSuggestedValue返回的值就是${xxx.documentcenter.bucketName:ssdocumentcenterstatic},因為是字符串,所以進(jìn)入到下面的邏輯AbstractBeanFactory的resolveEmbeddedValue(@Nullable String value)方法中。resolveEmbeddedValue將完成@Value注解值的解析過程,最終得到配置文件中配置的值ssdocumentcenterstatic。下面的TypeConverter的convertIfNecessary主要完成數(shù)據(jù)類型的轉(zhuǎn)換,比如配置文件中配置的Boolean類型,將String轉(zhuǎn)為Boolean等。最后返回value值并注入到bean中,此成員屬性注入到bean的過程就完成了。我們簡單看一下resolveEmbeddedValue解析過程,主要通過AbstractPropertyResolver解析器完成,其內(nèi)部使用了默認(rèn)的占位符如下:
通過其doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)方法完成解析,內(nèi)部調(diào)用了PropertyPlaceholderHelper的parseStringValue(String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders)方法來具體實現(xiàn)注解的值的解析和占位替換。解析首先是從配置文件對應(yīng)的PropertySource中完成配置和值讀取,最終通過MapPropertySource實現(xiàn)。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
學(xué)習(xí)SpringBoot容器功能及注解原理
這篇文章主要介紹了學(xué)習(xí)SpringBoot容器功能及注解原理,文中通過詳細(xì)的代碼示例對SpringBoot容器功能及注解原理進(jìn)行了解析,有需要的朋友可以借鑒參考下2021-09-09Java 中String StringBuilder 與 StringBuffer詳解及用法實例
這篇文章主要介紹了Java 中String StringBuilder 與 StringBuffer詳解及用法實例的相關(guān)資料,需要的朋友可以參考下2017-02-02Spring?Boot中使用Spring?Retry重試框架的操作方法
這篇文章主要介紹了Spring?Retry?在SpringBoot?中的應(yīng)用,介紹了RetryTemplate配置的時候,需要設(shè)置的重試策略和退避策略,需要的朋友可以參考下2022-04-04Java 如何讀取Excel格式xls、xlsx數(shù)據(jù)工具類
這篇文章主要介紹了Java 如何讀取Excel格式xls、xlsx數(shù)據(jù)工具類的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Java中String字符串轉(zhuǎn)具體對象的幾種常用方式
String對象可以用來存儲任何字符串類型的數(shù)據(jù),包括HTML、XML等格式的字符串,下面這篇文章主要給大家介紹了關(guān)于JavaString字符串轉(zhuǎn)具體對象的幾種常用方式,需要的朋友可以參考下2024-03-03