欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring—@Value在static中引用方式

 更新時間:2023年09月21日 10:07:01   作者:花無名v5  
這篇文章主要介紹了Spring—@Value在static中引用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

@Value注解

spring在讀取yml、properties等文件中的配置時,可直接使用@Value注解。

而且@Value除了支持String,int等類型的數(shù)據(jù),還支持?jǐn)?shù)組、Map、bean多種類型數(shù)據(jù)注入,應(yīng)用起來非常方便。

不過在使用這個注解的過程中也有需要注意的點。

其中一點就是靜態(tài)屬性的注入時機,如果使用方法不當(dāng),靜態(tài)屬性無法完成屬性注入;第二點是需要用什么方式,才可以在在靜態(tài)方法、靜態(tài)代碼塊中獲取配置的屬性信息。

本文主要是對@Value在這兩種情況下的使用進行說明,同時簡單講解一下@Value注入的原理。

代碼加載順序

在進入@Value使用介紹之前,先說下對于包含靜態(tài)方法、靜態(tài)代碼塊、@PostConstruct方法、默認構(gòu)造方法的代碼執(zhí)行順序是什么樣的。

static修飾的靜態(tài)方法、代碼塊、靜態(tài)屬性,都是在類加載的時候就進行加載,并且靜態(tài)代碼塊是會主動執(zhí)行,靜態(tài)方法可以直接通過類名引用。

類實例是在類加載之后才進行的。

下邊的例子針對靜態(tài)代碼塊和默認構(gòu)造參數(shù)、PostConstruct方法進行對比,根據(jù)輸出結(jié)果可知靜態(tài)代碼塊是先加載,然后是默認構(gòu)造函數(shù),最后才是PostConstruct修飾的方法。所以如果想要在靜態(tài)代碼塊中使用spring注入的屬性需要做些特殊處理(后邊會講到)。

@Component
public class FuncLoadOrderService {
    //記錄每個步驟執(zhí)行的順序
    private static AtomicLong step = new AtomicLong(0);
    //默認構(gòu)造參數(shù)
    public FuncLoadOrderService() {
        System.out.println("construct run step " + step.getAndIncrement());
    }
    //靜態(tài)代碼塊
    static {
        System.out.println("static block run step " + step.getAndIncrement());
    }
    //
    @PostConstruct
    public void constructFunc() {
        System.out.println("PostConstruct run step " + step.getAndIncrement());
    }
}

結(jié)果輸出

static block run step 0
construct run step 1
PostConstruct run step 2

@Value屬性注入

普通屬性注入

此處說的普通屬性為非static變量,類似如下聲明

//這就是一個普通屬性
private String testConfigId;
  • 結(jié)論先行。
  • 對于這種注入方式,由于實例是在默認構(gòu)造參數(shù)執(zhí)行之后才會創(chuàng)建,且方法加載順序為 靜態(tài)代碼塊 --> 靜態(tài)方法 --> 默認構(gòu)造參數(shù) --> PostConstruct修飾的方法。
  • 所以普通屬性使用@Value注入的變量,只有在PostConstruct修飾的方法可以取到值,即只有對象bean完成了初始化才可以獲取到配置值。
  • 想在PostConstruct前的幾步中取到值需要直接讀取配置文件,加載內(nèi)容。

屬性的普通注入方式如下,直接使用@Value注解就可以注入配置文件中的配置。

@Value("${test.configId}")
private String testConfigId;

這種方式是工作中比較常用的注入方式了,但是因為value是在類實例創(chuàng)建之后才注入的,

所以這里有兩個注意點

  • 1.默認構(gòu)造參數(shù)中無法獲取注入的value
  • 2.static修飾的方法無法獲取注入的value

如果一定想要在默認構(gòu)造參數(shù)里獲取@Value注入的值怎么辦呢?想一想為什么默認構(gòu)造參數(shù)無法使用@Value注入的值~~

是不是因為此時bean還沒有創(chuàng)建,類對象還沒有實例化,所以所有依賴Bean創(chuàng)建方式來注入值的方式都不可以使用,因此可以考慮直接讀取配置文件來獲取值。

這里提供兩種方式。第一種是使用 YamlMapFactoryBean 將配置文件的內(nèi)容讀到map中

YamlMapFactoryBean yaml = new YamlMapFactoryBean();
yaml.setResources(new ClassPathResource("application.yml"));
Map<String, Object> configMap = yaml.getObject();

這個方式可以達到目的,但是數(shù)據(jù)是嵌套的map,沒有按照key展開,使用起來不是很方便。

比如配置文件中內(nèi)容如下,如果用YamlMapFactoryBean方式讀配置,只能先 map.get(“test”) 獲取返回值,進行類型轉(zhuǎn)換,再做其他處理

test:
  configId: 1oiieuu
  configMap: "{\"id\":123,\"key\":12333}"

第二種方式和第一個類似,只不過讀出來的數(shù)據(jù)是按照key做了展開。

這種方式使用的是 YamlPropertiesFactoryBean 。

YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("application.yml"));
Properties properties = yamlProperties.getObject();

對于上邊提到的同樣的配置,如果想讀configId,可以直接使用 properties.get(“test.configId”) / properties.getProperty(“test.configId”) 等方法。

使用 YamlPropertiesFactoryBean 的方式最接近直接用@Value,強烈推薦!!

static塊、默認構(gòu)造方法獲取@Value值

對于static代碼塊和默認構(gòu)造方法,想要獲取配置文件中的值,只能通過將配置文件讀到內(nèi)存,轉(zhuǎn)成map、properties等方法(也可以轉(zhuǎn)json、yaml等),來獲取值。

靜態(tài)方法中獲取@Value值

上邊講到普通的變量使用@Value修飾,在靜態(tài)方法中獲取不到值,是因為靜態(tài)方法中想要獲取普通變量,需要用new來創(chuàng)建對象,new出來的對象沒有注入@Value,所以想要在靜態(tài)方法中使用@Value修飾的對象,需要把對象定義為static類型,

代碼如下:

private static String configId;
@Value("${test.configId}")
public void setConfigId(String configId) {
      FuncLoadOrderService.configId = configId;
}
public static void staticFuncTest() {
      System.out.println("static func " + FuncLoadOrderService.configId);
}

原理

上邊我們先給出了在不同情況下使用@Value會出現(xiàn)的情況,并給出了解決方案,下邊我們來簡單看下@Value為什么有些情況不能完成value注入。

  • 結(jié)論先行。
  • 對于@Value修飾的屬性、方法,在底層處理的時候和@Autowired處理邏輯是一樣的。
  • 處理邏輯是由AutowiredAnnotationBeanPostProcessor類的內(nèi)部類,AutowiredFieldElement 和 AutowiredMethodElement進行具體邏輯處理。
  • 其中AutowiredFieldElement處理使用了相關(guān)注解的屬性
  • AutowiredMethodElement處理使用了相關(guān)注解的方法
  • 以屬性處理鏈路為例,給出處理邏輯的調(diào)用鏈
  • AutowiredFieldElement#inject#resolveDependency#doResolveDependency#convertIfNecessary
//AutowiredAnnotationBeanPostProcessor默認構(gòu)造方法,把此類可以處理的類型加入到列表中,從代碼中可以看到這個類可以處理 @Value、@Autowired、@Inject三種注解
public AutowiredAnnotationBeanPostProcessor() {
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

內(nèi)部類AutowiredFieldElement的關(guān)鍵源碼如下。

從源碼中可看到處理邏輯都是針對bean來進行處理,而static修飾的方法、屬性,是在bean中獲取不到,所以static屬性使用@Value無法注入對應(yīng)的值。

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
      Field field = (Field) this.member;
      Object value;
      if (this.cached) {
            //在緩存中,直接進行屬性處理
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
      }
      else {
            //設(shè)置屬性信息
            DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
            desc.setContainingClass(bean.getClass());
            Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
            Assert.state(beanFactory != null, "No BeanFactory available");
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            try {
                  value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
            }
            catch (BeansException ex) {
                  throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
            }
            synchronized (this) {
                  if (!this.cached) {
                        //加入緩存處理
                  }
            }
      }
      if (value != null) {
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
      }
}

AutowiredMethodElement和AutowiredFieldElement一樣,都是繼承自 InjectionMetadata.InjectedElement,所以對于使用@Value的方法能夠完成注入。

下邊這段代碼中configId能夠注入,就是因為@Value使用在方法上,在bean加載時會將configId裝配到bean中。

private static String configId;
@Value("${test.configId}")
public void setConfigId(String configId) {
      FuncLoadOrderService.configId = configId;
}
public static void staticFuncTest() {
      System.out.println("static func " + FuncLoadOrderService.configId);
}

結(jié)論

@Value是平時用的比較多的注解,使用時也會遇到某些情況注入失敗,所以進行了一番了解并將了解的結(jié)論分享給大家。

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

相關(guān)文章

  • Java中transient關(guān)鍵字的詳細總結(jié)

    Java中transient關(guān)鍵字的詳細總結(jié)

    本文要介紹的是Java中的transient關(guān)鍵字,transient是短暫的意思。對于transient 修飾的成員變量,在類的實例對象的序列化處理過程中會被忽略,感興趣的朋友可以參考閱讀
    2023-04-04
  • Java實現(xiàn)考試系統(tǒng)

    Java實現(xiàn)考試系統(tǒng)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)考試系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • SpringCloud使用feign調(diào)用錯誤的問題

    SpringCloud使用feign調(diào)用錯誤的問題

    這篇文章主要介紹了SpringCloud使用feign調(diào)用錯誤的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • JAVA中的OutputStreamWriter流解析

    JAVA中的OutputStreamWriter流解析

    這篇文章主要介紹了JAVA中的OutputStreamWriter流解析,OutputStreamWriter提供了一種方便的方式將字符數(shù)據(jù)寫入到輸出流中,并進行字符編碼轉(zhuǎn)換,它是Java中處理字符流和字節(jié)流之間轉(zhuǎn)換的重要工具之一,需要的朋友可以參考下
    2023-10-10
  • spring security自定義決策管理器

    spring security自定義決策管理器

    這篇文章主要介紹了spring security自定義決策管理器的實現(xiàn)代碼,需要的朋友參考下吧
    2017-09-09
  • java接收文件流+response.body()調(diào)用兩次問題(分別接收文件和對象)

    java接收文件流+response.body()調(diào)用兩次問題(分別接收文件和對象)

    這篇文章主要介紹了java接收文件流+response.body()調(diào)用兩次問題(分別接收文件和對象),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 完美解決Get和Post請求中文亂碼的問題

    完美解決Get和Post請求中文亂碼的問題

    下面小編就為大家?guī)硪黄昝澜鉀QGet和Post請求中文亂碼的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • java并發(fā)編程_線程池的使用方法(詳解)

    java并發(fā)編程_線程池的使用方法(詳解)

    下面小編就為大家?guī)硪黄猨ava并發(fā)編程_線程池的使用方法(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • spring boot如何加入mail郵件支持

    spring boot如何加入mail郵件支持

    這篇文章主要介紹了spring boot如何加入mail郵件支持,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • Java基礎(chǔ)之反射原理與用法詳解

    Java基礎(chǔ)之反射原理與用法詳解

    這篇文章主要介紹了Java基礎(chǔ)之反射原理與用法,結(jié)合實例形式詳細分析了java反射的相關(guān)概念、原理、使用方法與操作注意事項,需要的朋友可以參考下
    2020-02-02

最新評論