解決@Value注解不能注入static修飾的屬性問(wèn)題
@Value注解不能注入static屬性
問(wèn)題描述
在application.yml中:
constant: ? key: hello ? value: world
工具類ConstantHelper:
@Component public class ConstantHelper { ? ? @Value("${constant.value}") ? ? private static String value; ? ? private static String key; ? ? public static String getValue() { ? ? ? ? return value; ? ? } ? ? public void setValue(String value) { ? ? ? ? ConstantHelper.value = value; ? ? } ? ? public static String getKey() { ? ? ? ? return key; ? ? } ? ? @Value("${constant.key}") ? ? public void setKey(String key) { ? ? ? ? ConstantHelper.key = key; ? ? } }
測(cè)試類:
@RequestMapping("/getConfig") public Map<String, Object> getConfig(){ ? ? Map<String,Object> map = new HashMap<>(); ? ? map.put("key", ConstantHelper.getKey()); ? ? map.put("value",ConstantHelper.getValue()); ? ? return map; }
結(jié)果:
{
"value": null,
"key": "hello"
}
可以發(fā)現(xiàn),@Value注解放在屬性上注入值失敗,而@Value放在setter方法上(注意,該方法也不能是靜態(tài)方法)卻能注入成功。為什么??
剖析
答案就在AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata方法中,AutowiredAnnotationBeanPostProcessor主要處理了@Autowired和@Value注解等等:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { ?? ??? ?List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); ?? ??? ?Class<?> targetClass = clazz; ?? ??? ?do { ?? ??? ??? ?final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ?? ??? ??? ?ReflectionUtils.doWithLocalFields(targetClass, field -> { ?? ??? ??? ??? ?AnnotationAttributes ann = findAutowiredAnnotation(field); ?? ??? ??? ??? ?if (ann != null) { ? ? ? ? ? //here!! ?? ??? ??? ??? ??? ?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 AutowiredFieldElement(field, required)); ?? ??? ??? ??? ?} ?? ??? ??? ?}); ?? ??? ??? ?ReflectionUtils.doWithLocalMethods(targetClass, method -> { ?? ??? ??? ??? ?Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); ?? ??? ??? ??? ?if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { ?? ??? ??? ??? ??? ?return; ?? ??? ??? ??? ?} ?? ??? ??? ??? ?AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); ?? ??? ??? ??? ?if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { ? ? ? ? ? //here!! ?? ??? ??? ??? ??? ?if (Modifier.isStatic(method.getModifiers())) { ?? ??? ??? ??? ??? ??? ?if (logger.isInfoEnabled()) { ?? ??? ??? ??? ??? ??? ??? ?logger.info("Autowired annotation is not supported on static methods: " + method); ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ??? ?return; ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?if (method.getParameterCount() == 0) { ?? ??? ??? ??? ??? ??? ?if (logger.isInfoEnabled()) { ?? ??? ??? ??? ??? ??? ??? ?logger.info("Autowired annotation should only be used on methods with parameters: " + ?? ??? ??? ??? ??? ??? ??? ??? ??? ?method); ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?boolean required = determineRequiredStatus(ann); ?? ??? ??? ??? ??? ?PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); ?? ??? ??? ??? ??? ?currElements.add(new AutowiredMethodElement(method, required, pd)); ?? ??? ??? ??? ?} ?? ??? ??? ?}); ?? ??? ??? ?elements.addAll(0, currElements); ?? ??? ??? ?targetClass = targetClass.getSuperclass(); ?? ??? ?} ?? ??? ?while (targetClass != null && targetClass != Object.class); ?? ??? ?return new InjectionMetadata(clazz, elements); ?? ?}
The conceptual problem here is that annotation-driven injection happens for each bean instance. So we shouldn’t inject static fields or static methods there because that would happen for every instance of that class. The injection lifecycle is tied to the instance lifecycle, not to the class lifecycle. Bridging between an instance’s state and static accessor - if really desired - is up to the concrete bean implementation but arguably shouldn’t be done by the framework itself.
從源碼上發(fā)現(xiàn),理論上spring是可以對(duì)靜態(tài)域注入的,只是spring沒(méi)有這樣做,它認(rèn)為依賴注入發(fā)生的時(shí)段是在實(shí)例的生命周期,而不是類的生命周期
@Value(“${屬性}“)注入被static修飾的屬性
場(chǎng)景:
通過(guò)httpclient調(diào)用第三方接口的時(shí)候,ip和端口不確定
需求:
寫一個(gè)工具類,可以動(dòng)態(tài)配置ip和端口來(lái)修改調(diào)用的地址和端口,要求工具類方法可以靜態(tài)調(diào)用。
問(wèn)題描述
static 不能和注解并用,被static修飾的成員變量,無(wú)法通過(guò)@Value注解動(dòng)態(tài)獲取到
解決方案
通過(guò)注入到set方法實(shí)現(xiàn)屬性動(dòng)態(tài)賦值
application.yml配置:
key: ? box: ? ? ip: 192.168.1.166 ? ? port: 9987
錯(cuò)誤代碼:
@Value("${key.box.ip}") private static String ip ; @Value("${key.box.port}") private static String port;
這樣寫的話,你會(huì)發(fā)現(xiàn)拿到的結(jié)果還是null
正確代碼:
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Map; /** * Created in 2021/6/29 15:07 * @author */ @Slf4j @Component public class KeyBoxHttpClientUtil { private static String ip ; private static String port; @Value("${key.box.ip}") public void setIP(String ip) { KeyBoxHttpClientUtil.ip = ip; } @Value("${key.box.port}") public void setPort(String port) { KeyBoxHttpClientUtil.port = port; } }
Tips:調(diào)整代碼之后,工具類必須使用@Component注解來(lái)修飾,否則依然無(wú)法獲取到結(jié)果。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于Springboot2.3訪問(wèn)本地路徑下靜態(tài)資源的方法(解決報(bào)錯(cuò):Not allowed to load local
這篇文章主要介紹了基于Springboot2.3訪問(wèn)本地路徑下靜態(tài)資源的方法(解決報(bào)錯(cuò):Not allowed to load local resource),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Java隨機(jī)密碼生成并和郵箱、手機(jī)號(hào)匹配
這篇文章主要介紹了Java隨機(jī)密碼生成并和郵箱、手機(jī)號(hào)匹配的相關(guān)資料,需要的朋友可以參考下2016-01-01eclipse springboot工程打war包方法及再Tomcat中運(yùn)行的方法
這篇文章主要介紹了eclipse springboot工程打war包方法及再Tomcat中運(yùn)行的方法,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08Java 實(shí)戰(zhàn)練習(xí)之網(wǎng)上電商項(xiàng)目的實(shí)現(xiàn)
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+vue+Springboot+ssm+mysql+maven+redis實(shí)現(xiàn)一個(gè)網(wǎng)上電商項(xiàng)目,大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11啟用springboot security后登錄web頁(yè)面需要用戶名和密碼的解決方法
這篇文章主要介紹了啟用springboot security后登錄web頁(yè)面需要用戶名和密碼的解決方法,也就是使用默認(rèn)用戶和密碼登錄的操作方法,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02詳解在Spring3中使用注解(@Scheduled)創(chuàng)建計(jì)劃任務(wù)
本篇文章主要介紹了詳解在Spring3中使用注解(@Scheduled)創(chuàng)建計(jì)劃任務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03Java中數(shù)組如何轉(zhuǎn)為字符串的幾種方法
數(shù)組是java中一個(gè)重要的類型,小伙伴們知道如何將數(shù)組轉(zhuǎn)為字符串嗎,這篇文章主要給大家介紹了關(guān)于Java中數(shù)組如何轉(zhuǎn)為字符串的幾種方法,需要的朋友可以參考下2024-03-03DoytoQuery中的關(guān)聯(lián)查詢方案示例詳解
這篇文章主要為大家介紹了DoytoQuery中的關(guān)聯(lián)查詢方案示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12