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
,还有 JDBC
、SVN
等方式
@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中。