1. 程式人生 > >Nacos Config客戶端與Spring Boot、Spring Cloud深度整合

Nacos Config客戶端與Spring Boot、Spring Cloud深度整合

目錄

  • Nacos與Spring Boot整合
    • @NacosPropertySource和@NacosValue
      • com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor
      • com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor
      • @NacosValue

從原始碼角度,解析Nacos Config客戶端與Spring Boot、Spring Cloud的深度整合
原創博文,轉載請註明來源

Nacos與Spring Boot整合

@NacosPropertySource和@NacosValue

@PropertySource的用法並不陌生,它是spring原生的註解,我們可以這麼用:

@Configuration
@PropertySource(value = "classpath:demo.properties",ignoreResourceNotFound = false)
public class SpringPropertysourceApplication {
   //...
}

意思是:把在classpath路徑下,名為demo.properties的配置檔案注入到spring容器中,這樣,我們就可以直接在類的屬性上通過@Value註解獲取到demo.properties屬性值了。
Nacos為了達到以上目的,提供了一個叫@NacosPropertySource的註解,和@PropertySource目的一樣:把配置注入到spring容器;使用方式一樣,用於任意被spring管理的類上。當然,Nacos提供了更高階的功能,比如Property變更,自動重新整理的功能,下面來分析一下,Nacos是怎麼整合的

com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor

這個類實現了org.springframework.beans.factory.config.BeanFactoryPostProcessor(Spring鉤子,它在所有spring bean定義生成後,例項化之前呼叫,允許覆蓋或新增其屬性)等介面,主要作用是,掃描由spring所有的bean,檢視其類上,是否有@NacosPropertySource註解,如果有的話,則生成com.alibaba.nacos.spring.core.env.NacosPropertySource例項物件(@NacosPropertySource註解標註了當前PropertySource指定的DataId,也就是一個完成的配置檔案,生成例項其實就是呼叫Nacos原生API獲取配置構造NacosPropertySource物件),再把例項新增到spring env.PropertySources中去,其實完成這幾步,我們就可以通過使用@Value或者ENV.getProperty()這種方式獲取到由Nacos管理的配置項了。
NacosPropertySourcePostProcessor程式碼片段:

上面的功能僅僅是把Nacos的配置注入到spring中,那動態重新整理的功能怎麼做的呢。

回顧一下,原生的Nacos sdk是怎麼樣監聽配置變化的

ConfigService configService = NacosFactory.createConfigService(properties);
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
configService.addListener(dataId, group, new Listener() {
    @Override
    public void receiveConfigInfo(String configInfo) {
        System.out.println("recieve:" + configInfo);
    }
    @Override
    public Executor getExecutor() {
        return null;
    }
});

可以看到,監聽器與配置檔案(DataId)是一對一的,所以,對於一個NacosPropertySource來說,應該有一個對應的監聽器,在上訴NacosPropertySourcePostProcessor的程式碼片段截圖中可以看到對應的程式碼:

這個新增listener的邏輯可以根據上面Nacos sdk的用法得出:

可以看到,在listener回撥的邏輯裡面,當有配置變更時會重新生成NacosPropertySource並替換掉ENV中過時的NacosPropertySource,完成這個部分的邏輯,我們通過ENV.getProperty()就可以動態獲取到屬性值了,但是通過@Value方式注入的到Bean物件的配置項,由於Bean已經生成,還是沒辦法動態更新,那Nacos是怎麼做的?

com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor

上面說到如果通過ENV獲取配置項的話,已經可以做到動態的目的了,但是如果此時持有配置項的Bean已經生成,則需要通過反射的機制,去動態更新了,從功能設計角度舉個例子來講清原理:
有類TestController,通過@Value的方式把demo.properties中app.config.threshold的配置項注入到屬性threshold,那應該是這樣的:

@RestController
@PropertySource(value = "classpath:demo.properties")
public class TestController {
    @Value("${app.config.threshold}")
    private String threshold;
 
 
}

那如果要通過反射設定屬性的話那就需要這麼一個對映關係:

app.config.threshold -> TestController.threshold

所以如果把這個對映關係儲存在記憶體,當listener回撥通知的時候,找到配置中的對應屬性,反射設定進去就好了。

Nacos也是這麼做的:

NacosValueAnnotationBeanPostProcessor實現了org.springframework.beans.factory.config.BeanPostProcessor(spring鉤子函式,當bean物件例項化完成,注入容器之前呼叫 ),在其Object postProcessBeforeInitialization(Object bean, String beanName) 方法中,我們可以解析bean中所有注有@Value的註解,並將上訴對映關係,儲存在記憶體中:

其中doWithFields實現如下:

其中有一點需要注意的地方,Nacos並沒有使用原生的@Value註解去達到動態重新整理的目的,因為違背了spring使用@Value的初衷,nacos自己實現了@NacosValue的註解

綜上所訴,@NacosPropertySource和@NacosValue組合使用達到動態配置的效果是這樣實現的:

@NacosValue

前面解析了@NacosPropertySource和@NacosValue組合使用達到動態配置原理,遺漏了一個細節點就是使用自定義註解@NacosValue是怎麼在bean初始化的過程中注入屬性的(前面說的動態重新整理,是通過反射設定的,是建立在bean已經初始化完畢的基礎上)。
還是com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor這個類,除了上面說的postProcessBeforeInitialization建立配置項和屬性的對映關係這個方法外,還有兩個方法,就是用於@NacosValue在bean初始過程中注入屬性的:

簡單理解一下這兩個方法的目的:

  • doGetInjectedBean首先獲取了@NacosValue中的配置項比如app.config.threshold,通過beanFactory解析出配置項對應的值(在ENV中),Member是一個隊Field和Method的抽象類,如果Mem是Field則把取出的值進行轉換和Field保持一致,如果是方法,則取出方法引數的Field進行轉換

  • buildInjectedObjectCacheKey用於對doGetInjectedBean方法中已經轉換過的值生一個cacheKey,這樣就不用做多次轉換的無用功

這兩個方法都不是spring的鉤子函式的方法,是在alibaba的spring-context-support包下,抽象類com.alibaba.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor提供的,這個類的作用是:解析被子類泛型指定的註解(public class NacosValueAnnotationBeanPostProcessor extends ValueAnnotationBeanPostProcessor

AnnotationInjectedBeanPostProcessor這個類的實現,參照了org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor的實現,而這個類就是用於Spring原生註解@Autowired 和@Value在bean初始化過程中注入依賴