前言
今天这篇文章我们来分析一下Spring Cloud Config 配置中心的源码,这应该是Spring Cloud Netflix的源码分析的最后一篇。下一个系列我将会继续分析Spring Cloud Alibaba相关组件的源码。Spring Cloud Config 基础使用请移步 《配置中心Spring Cloud Config》
回顾
配置中心的工作流程:微服务 -> 配置中心 -> Git仓库
简单回顾一下配置中心的搭建流程,首先需要导入spring-cloud-config-server
配置中心依赖,然后启动类需要注解上@EnableConfigServer
,
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerApplication1070
{
public static void main( String[] args )
{
SpringApplication.run(ConfigServerApplication1070.class);
}
}
然后使用bootstrap.yml
配置Git仓库,比如:
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/little_wolf/springcloud-config.git #配置远程仓库地址,去仓库中复制
username: 1462163787@qq.com #仓库是私有的需要账号
password: 密码
这样的话配中心就自然回去Git仓库下载配置文件,比如我们访问:http://localhost:1070/application-pay-dev.yml
,配置中心就回去Git下载application-pay-dev.yml配置文件,当然前台是Git仓库中是有这个文件的,如:
@EnableConfigServer注解
该注解的作用是启动config服务端,注解源码如下
/**
* @author Dave Syer
* @author
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigServerConfiguration.class)
public @interface EnableConfigServer {
}
注解上有一个@Import(ConfigServerConfiguration.class) , ConfigServerConfiguration的作用只是作为ConfigServer自动配置的开启条件,如下:
/**
* @author Spencer Gibb
*/
@Configuration
public class ConfigServerConfiguration {
class Marker {
}
@Bean
public Marker enableConfigServerMarker() {
return new Marker();
}
}
这个类里面貌似啥都没有做,只是注册了一个叫Marker的Bean而已
ConfigServerAutoConfiguration自动配置
ConfigServerAutoConfiguration是Config的自动配置类,通过SpingBoot自动配置流程注册到Spring容器中【SpringBoot自动配置说过很多次了这里不在赘述了】,它位于spring-cloud-config-server这个jar中
我们看一下这个自动配置类中有什么
@Configuration
//条件,如果容器总中有 Marker 这个bean,该类就可以起作用
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
//开启config的配置,ConfigServerProperties用来加载yml中的config配置
@EnableConfigurationProperties(ConfigServerProperties.class)
//导入了一些config相关的配置
@Import({
EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,
ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class })
public class ConfigServerAutoConfiguration {
}
在ConfigServerAutoConfiguration自动配置类中没有做任何配置,但是该类上大有文章
- @ConditionalOnBean(ConfigServerConfiguration.Marker.class):这个是一个配置生效的前置条件,如果Sping容器中存在ConfigServerConfiguration.Marker这个B ean,ConfigServerAutoConfiguration配置类才生效,是不是和上面ConfigServerConfiguration联系起来了
- @EnableConfigurationProperties(ConfigServerProperties.class):ConfigServerProperties是用来加载配置文件中
spring.cloud.config.server
开头的配置中心配置项目 - EnvironmentRepositoryConfiguration:这个是仓库环境配置,比如基于Git的仓库配置,基于本地的仓库配置,基于SVN的仓库配置,基于JDBC的仓库配置,或者多种仓库复合配置。
- ResourceRepositoryConfiguration:这个是资源仓库配置,其中注册了 ResourceRepository这个Bean,其作用就是用来将载配置文件加载成为Resource对象。
- ConfigServerEncryptionConfiguration:配置中心的加密配置,该类中注册了一个 EncryptionController 控制器,这个是用来处理针对有加密的配置的请求。
ConfigServerMvcConfiguration:配置中心的MVC配置类,其中注册了一个 EnvironmentController ,这个类也是一个控制器,我们向配置中心发起的获取配置文件的请求就是它来处理的,这个我们要重点分析。
MultipleJGitEnvironmentRepository 配置
在EnvironmentRepositoryConfiguration配类中有针对于各种仓库的配置
@Configuration @EnableConfigurationProperties({ SvnKitEnvironmentProperties.class, JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class }) @Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultRepositoryConfiguration.class, SvnRepositoryConfiguration.class, NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class, DefaultRepositoryConfiguration.class }) public class EnvironmentRepositoryConfiguration { ...省略部分代码....... @Configuration @ConditionalOnClass(TransportConfigCallback.class) static class JGitFactoryConfig { //基于一个或多个git存储库的EnvironmentRepository 配置工厂 @Bean public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory( ConfigurableEnvironment environment, ConfigServerProperties server, Optional<ConfigurableHttpConnectionFactory> jgitHttpConnectionFactory, Optional<TransportConfigCallback> customTransportConfigCallback) { return new MultipleJGitEnvironmentRepositoryFactory(environment, server, jgitHttpConnectionFactory, customTransportConfigCallback); } } //Http客户端配置,发网络请求的时候用到 @Configuration @ConditionalOnClass({ HttpClient.class, TransportConfigCallback.class }) static class JGitHttpClientConfig { @Bean public ConfigurableHttpConnectionFactory httpClientConnectionFactory() { return new HttpClientConfigurableHttpConnectionFactory(); } } ...省略部分 代码....... //[重要] git的仓库配置 @Configuration @Profile("git") class GitRepositoryConfiguration extends DefaultRepositoryConfiguration { } //svn仓库配置 @Configuration @Profile("subversion") class SvnRepositoryConfiguration { @Bean public SvnKitEnvironmentRepository svnKitEnvironmentRepository(SvnKitEnvironmentProperties environmentProperties, SvnEnvironmentRepositoryFactory factory) { return factory.build(environmentProperties); } } //针对于JDBC的仓库配置 @Configuration @Profile("jdbc") @ConditionalOnClass(JdbcTemplate.class) class JdbcRepositoryConfiguration { ...省略部分代码...
我们重点跟一下 GitRepositoryConfiguration 这个针对于Git的仓库配置,因为我们使用的是这个, 该配置类继承了 DefaultRepositoryConfiguration
@Configuration @ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT) class DefaultRepositoryConfiguration { @Autowired private ConfigurableEnvironment environment; @Autowired private ConfigServerProperties server; @Autowired(required = false) private TransportConfigCallback customTransportConfigCallback; //注册MultipleJGitEnvironmentRepository,基于一个或多个git存储库的EnvironmentRepository @Bean public MultipleJGitEnvironmentRepository defaultEnvironmentRepository( MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory, MultipleJGitEnvironmentProperties environmentProperties) throws Exception { return gitEnvironmentRepositoryFactory.build(environmentProperties); } }
DefaultRepositoryConfiguration中注册了一个 MultipleJGitEnvironmentRepository类,该类实现于 EnvironmentRepository ,基于一个或多个git存储库的EnvironmentRepository ,功能比如:从磁盘加载配置文件,去git克隆一个配置文件等的呢
EnvironmentController 控制器
我们先看一下 ConfigServerMvcConfiguration 配置类的源码
@Configuration
@ConditionalOnWebApplication
public class ConfigServerMvcConfiguration extends WebMvcConfigurerAdapter {
@Autowired(required = false)
private EnvironmentEncryptor environmentEncryptor;
@Autowired(required = false)
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("properties", MediaType.valueOf("text/plain"));
configurer.mediaType("yml", MediaType.valueOf("text/yaml"));
configurer.mediaType("yaml", MediaType.valueOf("text/yaml"));
}
//【重要】 注册 EnvironmentController
@Bean
public EnvironmentController environmentController(EnvironmentRepository envRepository, ConfigServerProperties server) {
EnvironmentController controller = new EnvironmentController(encrypted(envRepository, server), this.objectMapper);
controller.setStripDocumentFromYaml(server.isStripDocumentFromYaml());
controller.setAcceptEmpty(server.isAcceptEmpty());
return controller;
}
...省略部分代码...
该配置类中注册了 EnvironmentController ,我们向配置中心发起的加载配置文件的请求 http://localhost:1070/application-pay-dev.yml
就是它来处理的,我们继续看它的源码
@RestController
@RequestMapping(method = RequestMethod.GET, path = "${spring.cloud.config.server.prefix:}")
public class EnvironmentController {
private EnvironmentRepository repository;
private ObjectMapper objectMapper;
private boolean stripDocument = true;
private boolean acceptEmpty = true;
public EnvironmentController(EnvironmentRepository repository) {
this(repository, new ObjectMapper());
}
public EnvironmentController(EnvironmentRepository repository,
ObjectMapper objectMapper) {
this.repository = repository;
this.objectMapper = objectMapper;
}
/**
* Flag to indicate that YAML documents which are not a map should be stripped of the
* "document" prefix that is added by Spring (to facilitate conversion to Properties).
*
* @param stripDocument the flag to set
*/
public void setStripDocumentFromYaml(boolean stripDocument) {
this.stripDocument = stripDocument;
}
/**
* Flag to indicate that If HTTP 404 needs to be sent if Application is not Found
*
* @param acceptEmpty the flag to set
*/
public void setAcceptEmpty(boolean acceptEmpty) {
this.acceptEmpty = acceptEmpty;
}
@RequestMapping("/{name}/{profiles:.*[^-].*}")
public Environment defaultLabel(@PathVariable String name,
@PathVariable String profiles) {
return labelled(name, profiles, null);
}
//[重要] 请求 http://xxx:port/application-xx-dev.yml 走的这里
@RequestMapping("/{name}/{profiles}/{label:.*}")
public Environment labelled(@PathVariable String name, @PathVariable String profiles,
@PathVariable String label) {
if (name != null && name.contains("(_)")) {
// "(_)" is uncommon in a git repo name, but "/" cannot be matched
// by Spring MVC
name = name.replace("(_)", "/");
}
if (label != null && label.contains("(_)")) {
// "(_)" is uncommon in a git branch name, but "/" cannot be matched
// by Spring MVC
label = label.replace("(_)", "/");
}
Environment environment = this.repository.findOne(name, profiles, label);
if(!acceptEmpty && (environment == null || environment.getPropertySources().isEmpty())){
throw new EnvironmentNotFoundException("Profile Not found");
}
return environment;
}
重点看EnvironmentController#labelled方法,它有两个参数 ,name和profiles,加入请求的配置文件是:http://xxx:port/application-pay-dev.yml
那么
- name: 就是 application-pay 文件名
- profiles: 就是 dev 环境名
- label:分支,如果没有指定就是null,后面分支默认会使用master
该方法接收到请求的文件名,然后做了一些符号的处理,把(_)替换成“/” , 然后调用 EnvironmentRepository.findOne方法加载配置,返回一个Environment对象。
Environment 配置的封装
Environment 对象是对 文件名(name),环境名(profiles),分支(label)的封装。
public class Environment {
//文件名
private String name;
//环境名 profile
private String[] profiles = new String[0];
//分支
private String label;
//【重要】加载到的配置内容其实是在PropertySource中
private List<PropertySource> propertySources = new ArrayList<>();
private String version;
private String state;
...省略...
}
PropertySource 资源配置
//【重要】用来封装配置内容的资源对象
public class PropertySource {
//文件名:如 https://gitee.com/little_wolf/springcloud-config-1107.git/application-pay-dev.yml
private String name;
//一个map用来装配置文件中的所有的配置项目
private Map<?, ?> source;
文章就先到这里把,喜欢的话给个好评哦!!!