文章目錄
- 1. 注解EnableConfigServer
- 2. ConfigServerAutoConfiguration
- 2.1 @ConditionalOnBean和@ConditionalOnProperty
- 2.2 @Import注解
- 2.2.1. EnvironmentRepositoryConfiguration.class
- 2.2.2. CompositeConfiguration.class
- 2.2.3. ResourceRepositoryConfiguration.class
- 2.2.4. ConfigServerEncryptionConfiguration.class
- 2.2.5. ConfigServerMvcConfiguration.class
- 2.2.6. ResourceEncryptorConfiguration.class
- 3. EnvironmentRepository
- 4. EnvironmentRepositoryConfiguration.class
- 5. rest接口
spring cloud config server 作為一個spring boot工程,到底是如何運行起來的?似乎如上一篇文章中那樣,引入了starter,啟動了注解,配置了git的信息,就可以獲取到數據了。那具體的原理是什么呢?
1. 注解EnableConfigServer
@EnableConfigServer
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigServer {public static void main(String[] args) {SpringApplication.run(ConfigServer.class, args);}
}
可以看到啟動類上加入注解@EnableConfigServer。我們查看該注解的源碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigServerConfiguration.class)
public @interface EnableConfigServer {}
這個注解的定義當中,通過Import注解加載Bean ConfigServerConfiguration.class :
@Configuration(proxyBeanMethods = false)
public class ConfigServerConfiguration {@Beanpublic Marker enableConfigServerMarker() {return new Marker();}class Marker {}}
這個Configuration類只是加載了一個 Bean Marker。
這是spring加載Bean的一種常用方式。
2. ConfigServerAutoConfiguration
在spring cloud config Server的jar中,org.springframework.boot.autoconfigure.AutoConfiguration.imports文件內包含了多個自動配置的類,其中就包括ConfigServerAutoConfiguration類。
org.springframework.cloud.config.server.bootstrap.ConfigServerBootstrapOverridesAutoConfiguration
org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration
org.springframework.cloud.config.server.config.RsaEncryptionAutoConfiguration
org.springframework.cloud.config.server.config.DefaultTextEncryptionAutoConfiguration
org.springframework.cloud.config.server.config.EncryptionAutoConfiguration
org.springframework.cloud.config.server.config.VaultEncryptionAutoConfiguration
根據命名,可以看到各個自動配置類的功能,是啟用bootstrap配置還是啟用RSA加密等等。這里暫時先關注
ConfigServerAutoConfiguration類:
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
@ConditionalOnProperty(name = ConfigServerProperties.PREFIX + ".enabled", matchIfMissing = true)
@EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class,ResourceEncryptorConfiguration.class })
public class ConfigServerAutoConfiguration {}
這個類的注解包含了三個重要的注解@ConditionalOnBean、@ConditionalOnProperty和@Import。
2.1 @ConditionalOnBean和@ConditionalOnProperty
當這兩個注解出現在同一個類上的時候,兩個Conditional條件必須同時滿足,即ConfigServerConfiguration.Marker.class這個Bean存在的同時,還要滿足Spring環境屬性中存在 ConfigServerProperties.PREFIX + “.enabled” 屬性且其值為 true,或者該屬性缺失(由于 matchIfMissing = true)。這個PREFIX是spring.cloud.config.server。
這里剛好好上面通過@EnableConfigServer加載的Bean Marker.class呼應上。
2.2 @Import注解
Import注解引入了六個類要加載到spring context中。
2.2.1. EnvironmentRepositoryConfiguration.class
EnvironmentRepositoryConfiguration 是Spring Cloud Config Server中配置存儲庫的核心配置類。它負責創建和配置EnvironmentRepository實例,EnvironmentRepository用于從各種后端(如Git、SVN、本地文件系統等)獲取配置屬性。
定義和配置不同類型的EnvironmentRepository(如GitEnvironmentRepository、NativeEnvironmentRepository)。
通過注入不同的配置屬性,來靈活配置不同類型的存儲庫。
2.2.2. CompositeConfiguration.class
CompositeConfiguration 負責配置和管理CompositeEnvironmentRepository。CompositeEnvironmentRepository允許將多個EnvironmentRepository組合在一起,以便從多個源獲取配置。
配置CompositeEnvironmentRepository,將多個EnvironmentRepository實例組合成一個邏輯上的存儲庫。
提供從多個配置源合并配置屬性的功能,以實現更復雜的配置管理場景。
2.2.3. ResourceRepositoryConfiguration.class
ResourceRepositoryConfiguration 負責配置與管理資源存儲庫(ResourceRepository)。ResourceRepository用于訪問和管理配置服務器上的靜態資源,如配置文件、密鑰等。
配置不同類型的ResourceRepository,如文件系統資源存儲庫或Git資源存儲庫。
通過REST接口提供資源訪問和管理功能。
2.2.4. ConfigServerEncryptionConfiguration.class
ConfigServerEncryptionConfiguration 負責配置加密和解密功能。它提供加密和解密配置屬性的能力,確保敏感數據在傳輸和存儲時得到保護。
配置加密器(TextEncryptor),用于加密和解密敏感配置信息。
提供加密和解密端點,允許客戶端通過API進行加密和解密操作。
2.2.5. ConfigServerMvcConfiguration.class
ConfigServerMvcConfiguration 配置Spring MVC相關的組件,為Spring Cloud Config Server提供RESTful API。它定義了Config Server的主要控制器和路由。
定義Config Server的REST API端點,處理配置屬性的請求。
配置HTTP請求處理、路由和控制器。
2.2.6. ResourceEncryptorConfiguration.class
ResourceEncryptorConfiguration 負責配置與資源加密相關的功能。它確保資源存儲庫中的敏感數據在存儲和訪問時得到加密保護。
配置用于資源加密和解密的組件。
提供加密資源的支持,確保靜態資源的安全性。
3. EnvironmentRepository
對于 Spring Cloud Config 而言,它把所有的配置信息抽象為一種 Environment(環境),而存儲這些配置信息的地方就稱為 EnvironmentRepository。
public interface EnvironmentRepository {Environment findOne(String application, String profile, String label);default Environment findOne(String application, String profile, String label, boolean includeOrigin) {return findOne(application, profile, label);}}
spring cloud中把配置信息抽象為application,profile和label三個維度來管理,即哪一個應用application在什么樣的環境profile下,使用哪一個label的配置數據。
4. EnvironmentRepositoryConfiguration.class
這個類里加載很多的內容,包括svn、git、jdbc等等的RepositoryConfiguration。其中有一個Configuration類是DefaultRepositoryConfiguration.class。這個是放在最后一個加載的配置,即默認的配置:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT)
class DefaultRepositoryConfiguration {@Beanpublic MultipleJGitEnvironmentRepository defaultEnvironmentRepository(MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,MultipleJGitEnvironmentProperties environmentProperties) throws Exception {return gitEnvironmentRepositoryFactory.build(environmentProperties);}}
這里是加載MultipleJGitEnvironmentRepository的Bean,由gitEnvironmentRepositoryFactory的build方法來構建:
public MultipleJGitEnvironmentRepository build(MultipleJGitEnvironmentProperties environmentProperties)throws Exception {if (this.connectionFactory.isPresent()) {HttpTransport.setConnectionFactory(this.connectionFactory.get());this.connectionFactory.get().addConfiguration(environmentProperties);}MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(this.environment,environmentProperties, ObservationRegistry.NOOP);repository.setTransportConfigCallback(transportConfigCallbackFactory.build(environmentProperties));if (this.server.getDefaultLabel() != null) {repository.setDefaultLabel(this.server.getDefaultLabel());}repository.setGitCredentialsProviderFactory(gitCredentialsProviderFactory);repository.getRepos().forEach((name, repo) -> repo.setGitCredentialsProviderFactory(gitCredentialsProviderFactory));return repository;}
而DefaultRepositoryConfiguration這個類,被GitRepositoryConfiguration繼承了。
@Configuration(proxyBeanMethods = false)
@Profile("git")
class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {}
也就是說 Spring Cloud Config 中默認使用 Git 作為配置倉庫來完成配置信息的存儲和管理,提供的 EnvironmentRepository 就是 MultipleJGitEnvironmentRepository,而 MultipleJGitEnvironmentRepository 則繼承了抽象類 JGitEnvironmentRepository。
當服務器啟動時,在 JGitEnvironmentRepository 中會決定是否調用 initClonedRepository() 方法來完成從遠程 Git 倉庫 Clone 代碼。如果執行了這一操作,相當于會將配置文件從 Git 上 clone 到本地,然后再進行其他的操作。在 JGitEnvironmentRepository 抽象類中,提供了大量針對第三方 Git 倉庫的操作代碼,無論采用諸如 Git、SVN 等具體某一種配置倉庫的實現方式,最終我們處理的對象都是位于本地文件系統中的配置文件。
在AbstractScmEnvironmentRepository類中
@Overridepublic synchronized Environment findOne(String application, String profile, String label) {return findOne(application, profile, label, false);}@Overridepublic synchronized Environment findOne(String application, String profile, String label, boolean includeOrigin) {NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),new NativeEnvironmentProperties(), this.observationRegistry);Locations locations = getLocations(application, profile, label);delegate.setSearchLocations(locations.getLocations());Environment result = delegate.findOne(application, profile, "", includeOrigin);result.setVersion(locations.getVersion());result.setLabel(label);return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());}
在上面的findOne方法中,調用了NativeEnvironmentRepository類的findOne方法:
@Overridepublic Environment findOne(String config, String profile, String label, boolean includeOrigin) {try {ConfigurableEnvironment environment = getEnvironment(config, profile, label);DefaultResourceLoader resourceLoader = new DefaultResourceLoader();Map<org.springframework.core.env.PropertySource<?>, PropertySourceConfigData> propertySourceToConfigData = new HashMap<>();ConfigDataEnvironmentPostProcessor.applyTo(environment, resourceLoader, null,StringUtils.commaDelimitedListToSet(profile), new ConfigDataEnvironmentUpdateListener() {@Overridepublic void onPropertySourceAdded(org.springframework.core.env.PropertySource<?> propertySource,ConfigDataLocation location, ConfigDataResource resource) {propertySourceToConfigData.put(propertySource,new PropertySourceConfigData(location, resource));}});environment.getPropertySources().remove("config-data-setup");return clean(ObservationEnvironmentRepositoryWrapper.wrap(this.observationRegistry, new PassthruEnvironmentRepository(environment)).findOne(config, profile, label, includeOrigin), propertySourceToConfigData);}catch (Exception e) {String msg = String.format("Could not construct context for config=%s profile=%s label=%s includeOrigin=%b",config, profile, label, includeOrigin);String completeMessage = NestedExceptionUtils.buildMessage(msg,NestedExceptionUtils.getMostSpecificCause(e));throw new FailedToConstructEnvironmentException(completeMessage, e);}}
我們看到最終委托 PassthruEnvironmentRepository 完成配置文件的讀取,然后通過 clean 方法完成本地文件地址與遠程倉庫之間地址的轉換。ConfigDataEnvironmentUpdateListener用于監聽Environment的更新。
5. rest接口
Server端獲取到了數據,是通過rest接口來提供給client的。這里EnvironmentController提供了rest接口:
@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",produces = MediaType.APPLICATION_JSON_VALUE)public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles) {return getEnvironment(name, profiles, null, false);}@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",produces = EnvironmentMediaType.V2_JSON)public Environment defaultLabelIncludeOrigin(@PathVariable String name, @PathVariable String profiles) {return getEnvironment(name, profiles, null, true);}@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = MediaType.APPLICATION_JSON_VALUE)public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label) {return getEnvironment(name, profiles, label, false);}@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = EnvironmentMediaType.V2_JSON)public Environment labelledIncludeOrigin(@PathVariable String name, @PathVariable String profiles,@PathVariable String label) {return getEnvironment(name, profiles, label, true);}
這里要注意path路徑的配置,在類注解上有@RequestMapping(method = RequestMethod.GET, path = “${spring.cloud.config.server.prefix:}”)可以配置前綴,不配置的話就是默認值“”。
在方法上的路徑,是/name/profile/label。這個正是配置文件中配置的內容。