Spring Cloud Config

前置知识:事件机制 应用上下文

Bootstrap 应用上下文

Bootstrap 上下文是 Spring Cloud 新引入的,与传统 Spring 上下文相同,即 ConfigurableApplicationContext 实例,由 BootstrapApplicationListener 监听 ApplicationEnvironmentPreparedEvent 事件时创建。

而由于 ConfigurableApplicationContext 继承 ApplicationContext,有了层次关系,或者说父子关系。

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
    // ...
}

通过 setParent(@Nullable ApplicationContext parent) 可以设置上下文的层级关系。

我们可以在 pom 文件中引入 Spring Boot 和 Spring Cloud 的依赖,启用 actuator

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.properties 中开启 Actuator Web 访问端口

management.server.port=8081
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

访问 http://127.0.0.1:8081/actuator/beans 可以看到 Spring Cloud Config 的上下文 是父,而 Spring Boot 的上下文是子。

我们也可以自定义我们自己的应用上下文,

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {

	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
	context.setId("心灵蚂蚁");
	context.registerBean("helloWorld", String.class, "hello,world"); // 注册bean给子类上下文使用
	context.refresh();
	new SpringApplicationBuilder(DemoApplication.class)
			.parent(context)
			.run(args);
//	SpringApplication.run(DemoApplication.class, args);
    }
}

为什么 Spring Cloud 上下文要比 Spring Boot 的上下文加载的早以及 BootstrapApplicationListener 是何时加载进来的

可以查看 事件机制

Spring Cloud Server

启动代码

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}
分析 @EnableConfigServer
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigServerConfiguration.class)
public @interface EnableConfigServer {

}

实际配置类 ConfigServerConfiguration

@Configuration
public class ConfigServerConfiguration {
    class Marker {}

    @Bean
    public Marker enableConfigServerMarker() {
	return new Marker();
    }
}

查看 使用 Marker 的地方,我们可以看到在 ConfigServerAutoConfiguration

@Configuration
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
@EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,
		ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class })
public class ConfigServerAutoConfiguration {

}

也就是通过注解 @EnableConfigServer 创建实例 Maker() 作为一个“开关”,然后在自动配置类 ConfigServerAutoConfiguration 中执行真正的代码逻辑。

Server 端支持什么方式获取配置信息

在类 EnvironmentRepositoryConfiguration 中,我们可以看到除了默认的 Git,还有 JDBCSVN等方式

@Configuration
@EnableConfigurationProperties({SvnKitEnvironmentProperties.class, CredhubEnvironmentProperties.class, JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class})
@Import({CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultConfiguration.class, VaultRepositoryConfiguration.class, CredhubConfiguration.class, CredhubRepositoryConfiguration.class, SvnRepositoryConfiguration.class, NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class, DefaultRepositoryConfiguration.class})
public class EnvironmentRepositoryConfiguration {
    // ...
}

查看 JdbcRepositoryConfiguration --> JdbcEnvironmentRepository --> EnvironmentRepository 可知,EnvironmentRepository 是核心接口,其下的实现有

JdbcEnvironmentRepository

EnvironmentRepository

JGitEnvironmentRepository

MultipleJGitEnvironmentRepository

MultipleJGitEnvironmentRepositoryFactory

EnvironmentRepositoryConfiguration

DefaultRepositoryConfiguration

为什么默认是 Git 作为配置仓库

@Configuration
@ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT)
class DefaultRepositoryConfiguration {
    ...
    @Bean
    public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(
	    MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,
	    MultipleJGitEnvironmentProperties environmentProperties) throws Exception {
	return gitEnvironmentRepositoryFactory.build(environmentProperties);
    }
}

当 Spring 应用上下文没有出现 EnvironmentRepository Bean 的时候,那么,默认激活 DefaultRepositoryConfiguration (Git 实现)

由此我们可以尝试下自定义 EnvironmentRepository 实现,用来替换默认的 JGitEnvironmentRepository

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.context.annotation.Bean;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootApplication
@EnableConfigServer
public class DemoApplication {

    public static void main(String[] args) {
	SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public EnvironmentRepository environmentRepository() {

//      return new EnvironmentRepository() {
//		@Override
//		public Environment findOne(String application, String profile, String label) {
//			return null;
//		}
//	};

	return (String application, String profile, String label) -> {
		Environment environment = new Environment("default", profile);
		List<PropertySource> propertySources = environment.getPropertySources();
		Map<String, Object> source = new HashMap<>();
		source.put("name", "心灵蚂蚁");
		PropertySource propertySource = new PropertySource("map", source);
		propertySources.add(propertySource);
		return environment;
	};
    }

}

浏览器访问获取配置

Spring Cloud Config

为什么客户端获取配置时需要写成 /{application}/{profile}/{label} 的格式

在类 EnvironmentController 中定义好的

@RestController
@RequestMapping(
    method = {RequestMethod.GET},
    path = {"${spring.cloud.config.server.prefix:}"}
)
public class EnvironmentController {
    // ...
    @RequestMapping(
        path = {"/{name}/{profiles}/{label:.*}"},
        produces = {"application/json"}
    )
    public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label) {
        // ...
    }
}

PropertySourceLocator

Spring Cloud 提供用于支持扩展自定义配置加载到spring Environment中。

参考:https://www.cnblogs.com/lizo/p/7683300.html

posted @ 2021-05-20 12:00  心灵蚂蚁  阅读(118)  评论(0编辑  收藏  举报