二十三.SpringCloudConfig源码-初始化配置

Posted 墨家巨子@俏如来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二十三.SpringCloudConfig源码-初始化配置相关的知识,希望对你有一定的参考价值。

前言

今天这篇文章我们来分析一下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;

文章就先到这里把,喜欢的话给个好评哦!!!

以上是关于二十三.SpringCloudConfig源码-初始化配置的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码分析(二十三)BeanFactory的后处理

我的jdk源码(二十三):ReentrantLock类

JDK源码(二十三):HashMap

android源码解析(二十三)-->Android异常处理流程

头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~

介绍开源的.net通信框架NetworkComms框架 源码分析(二十三 )TCPConnection