非spring boot 使用Spring cloud Config (老版 spring 集成 spring cloud config ) || spring cloud config 搭建 个人博客:https://eric-ly.github.io/
背景:想自己搭建一个分布式的配置中心,发现spring Cloud Config 不错,(后台基于git,可以配置webhook 实现热更新、回滚,分环境配置等)与自己的期望相近。
虽然好用但是基于spring boot的,spring 与spring boot 还是有挺多巨别。为了能用在现有的 项目中,决定 想办法集成一下,百度 google 都没有现成的可以copy...
于是想自己实现下,查询之后 按照一个大神的想法实现了下。中间遇到许多问题,不过更加了解spring了 也会继续完善。java 新人 希望各位大神多多指导。
github:https://github.com/Eric-ly/hconfig
后面有项目的测试说明:
实现结果:
在spring 中 使用spring Cloud Config 功能,分项目/环境 获取配置文件,自动注入,自动更新 spring boot的注解无法兼容 待稍后解决。
前提:
一个简单的spring spring MVC 项目。
config server 配置好,github 配置好。 这个网上百度就有很多。
(在搭建spring cloud config (server and client)后,搭建注册应用中心 eureka 和 rabbitMq 进行自动热更新。)
实现方式:
1.创建自定义的ApplicationContext,先通过在web.xml中设置 contextClass 来指定应用使用自定义的ApplicationContext,
2.在自定义的applicationContext中 覆写 创建environment的方法,用来定制 environment,把config server的配置信息加入 应用
3.自定义的CloudEnvironment extends StandardServletEnvironment,扩展customizePropertySources方法,指定 配置的来源,然后放入 propertySource的list中,作为整个项目的 配置之一。
4.自定义propertySouce ,使用spring cloud client的ConfigClientProperties 作为 propertySouce的来源, 然后添加到PropertySource中
5.自定义配置类,将配置中心的配置按照单个/model 进行注入
后期实现:
(1)兼容@refresh的注解,使 自动更新。
(2)通过修改name 获取多个配置文件。优化config server的文件
(3)细化 回滚功能
(4)写 拦截器,打成jar包,成为一个工具包,通过自定义注解 来使用。隐藏实现。成为一个通用的功能
最近有点忙,这个项目先达到能基本用,慢慢完善。
具体实现步骤:
1.增加pom 依赖,spring mvc ,spring cloud,logback 等
<properties>
<spring.version>4.2.9.RELEASE</spring.version>
<jstl.version>1.2</jstl.version>
<junit.version>4.11</junit.version>
<logback.version>1.0.13</logback.version>
<jcl-over-slf4j.version>1.7.5</jcl-over-slf4j.version>
<spring.cloud.version>1.2.0.RELEASE</spring.cloud.version>
</properties>
<dependencies>
<!-- junit 依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<!-- <scope>test</scope> -->
</dependency>
<!-- spring mvc 的 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring cloud config client 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>${spring.cloud.version}</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${jcl-over-slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
2.创建自定义的ApplicationContext,先通过在web.xml中设置 contextClass 来指定自己的ApplicationContext,
contextClass
org.lybm.hconfig.clientWosb.config.CustomWebApplicationContext
ApplicationContext 是spring ioc 的体现,spring 中的容器。一般有几种实现。 FileSystemXmlApplicationContext/ClassPathXmlApplicationContext/WebXmlApplicationContex 我们在搭建框架的时候一般通过注册ContextLoaderListener 监听 去自动获取 初始化(这里先去通过classContext的配置获取,而且必须可以强转为WebXmlApplicationContxt,如果没设置 创建默认的) ,这里为了使用spring boot的功能,我们需要自定义ApplicationContext
在新定义的ApplicationContext 中我们使用自定义的Environment,为了把配置放到 environment中来使用。
public class CustomWebApplicationContext extends XmlWebApplicationContext {
@Override
protected ConfigurableEnvironment createEnvironment() {
System.out.println("-------- loaded my CustomWebApplicationContext context");
return new CloudEnvironment();
}
}
3.创建 自定义的Environment,用来把 配置中心 config server的配置 放到环境中。
(1)重写定制方法
自定义的CloudEnvironment extends StandardServletEnvironment,扩展customizePropertySources方法,指定 配置的来源,然后放入 propertySource的list中,作为整个项目的 配置之一。
这里说一下,spring 的所有配置,包括system配置 ,jdni,自定义的properties等 都会放到 spring的environtment的propertySource的list中,用来使用。
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
try {
//用来添加应用名到environment中
propertySources.addLast( initResourcePropertySourceLocator() );
//添加config Server的配置
PropertySource<?> source = initConfigServicePropertySourceLocator(this);
propertySources.addLast(source);
} catch (Exception ex) {
logger.warn("failed to initialize cloud config environment", ex);
}
}
(2)获取 config server的 配置信息,生成propertySource ,创建一个来源。用到了spring cloud的方法
这里说明一下,在配置ConfigClientProperties 连接信息的时候, 后面的代码 会重新覆盖 applicationName 所以这里设置name是没有意义的。 会从environment 中 获取商品日嗯
private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) {
ConfigClientProperties configClientProperties = new ConfigClientProperties(environment);
configClientProperties.setUri("http://localhost:9001/");
// configClientProperties.setName("config-client");
configClientProperties.setProfile("dev");
configClientProperties.setLabel("master");
logger.debug("will load the client configuration-------"+configClientProperties);
ConfigServicePropertySourceLocator configServicePropertySourceLocator =
new ConfigServicePropertySourceLocator(configClientProperties);
return configServicePropertySourceLocator.locate(environment);
}
重新设置name的代码,如下, 这样会导致我们连接获取到的配置信息不正确。
override.setName(
environment.resolvePlaceholders("${" + ConfigClientProperties.PREFIX
+ ".name:${spring.application.name:application}}"));
于是,我在 进行这一步前 将正确的 name信息, 加入到environment中,就可以找到 正常使用。
(3)添加 应用相关配置 到environment中。
在查询相关源码后发现。我只需要 将一个resource 对象放入 environment的 资源 list中就可以,这里我新建一个properties,写入配置,然后封装成resource 就可以了。
cloud-config-context.properties spring.application.name=config-client demo=demo
具体实现:
Resource resource = new DefaultResourceLoader(this.getClass().getClassLoader()).
getResource("classpath:cloud-config-context.properties");
resourcePropertySource = new ResourcePropertySource(resource);
这里学习的过程 花费了一些时间。了解了 environment,propertySource 等spring 加载配置信息的代码。一些相关知识会在后续贴出来
4.现在environment 重写好了,创建配置信息类
我们目前可以通过两种方式获取配置,
(1)单个配置的获取。
//单独一个字段的获取
@Value("${test}")
private String test;
这么写 如果没有test ,程序会报错,启动不了, 所以 不要用这种写法
//单独一个字段的获取
@Value("${xxx:x}")
private String test;
单个字段 推荐这么写,如果xxx没有,则使用默认值x ,这样比较符合 业务场景
(2)按照配置信息类 获取
如下,将配置信息映射成一个model,可以将统一前缀 如wosb的配置信息 放到一个类里,这样方便管理,在写配置的时候 也好区分,就像一个一个配置文件。
@ConfigurationProperties(prefix = "wosb")
@Data
public class WosbProperties {
private String test;
private String demo;
private String name;
private String paht;
}
(2.2)将配置类 注入,两种方式,
a. 通过注解,增加@Configuration,将这个类注入到 容器中
注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
b.通过配置PropertySourcesPlaceHolderConfigurer,不太推荐
@Configuration
@EnableConfigurationProperties({WosbProperties.class})
public class PropertiesConfigurer {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
5.继承完成,使用/测试
(1)创建controller
(2)使用
@Autowired
private SearchProperties searchProperties;
//单独一个字段的获取
@Value("${xxx:x}")
private String test;
(3)可以注入Environment env 来查看当前的environment信息。
项目说明:
github:https://github.com/Eric-ly/hconfig
github的配置文件地址:https://github.com/Eric-ly/hconfig-properties
cloudeureka: spring boot的应用注册中心
ConfligClientNoSpringBoot: 非spring boot 应用 使用 应用配置中心
hconfigclient:spring boot的 应用配置中心的 客户端
hconfigserver:spring boot的 应用配置中心的服务端
使用说明:
1.启动 应用注册中心
(1)入口类:EurekaServerApplication IDE debug 或者用命令
(2)配置:
server.port=8761 eureka.instance.hostname=localhost
2.启动 配置中心的服务端
(1)入口类:ConfigServerApplication
(2)配置:
端口:
spring.application.name=config-server server.port=9001
gitlab :来源
spring.cloud.config.server.git.uri=https://github.com/Eric-ly/hconfig-properties.git spring.cloud.config.server.git.searchPaths=configRepo spring.cloud.config.server.git.username= spring.cloud.config.server.git.password= spring.cloud.config.server.git.basedir=src/main/resource/config # //加载到ConfigServer项目所在的本地目录的位置, 可以不用配置
消息总线,用来自动热更新,配置的是mq的地址,需要自己启
# 添加cloud bus 消息总线,配合webhook 实现 所有client的消息热更新 spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest ## 刷新时,关闭安全验证 management.security.enabled=false ## 开启消息跟踪 spring.cloud.bus.trace.enabled=true
注册中心:
#注册中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
3.启动 非spring boot的 ConfligClientNoSpringBoot
(1)配置 应用名
cloud-config-context.properties
spring.application.name=config-client
(2)IDE添加tomcat,然后启动
(3)测试
http://localhost:8888/hello
结果: 配置文件都读取到了
search-pproperties : collection :{{engine}} demo: {{solr}} ||| wosb-properties: demo : {{ a}} name: {{ wosb }} path: {{ /}} single value : test?{{ abcdegggg }}
4.spring boot的 配置中新 客户端( 自动热更新)hconfigclient
(1)入口类 entryApplication
(2)配置:
(1) 应用名+ 配置中心的 服务端
spring.application.name=config-client spring.cloud.config.label=master spring.cloud.config.profile=dev spring.cloud.config.uri= http://localhost:9001/ server.port=8888
(2)
#注册中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
(3)
## 刷新时,关闭安全验证 management.security.enabled=false ## 开启消息跟踪 spring.cloud.bus.trace.enabled=true spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest
(4) 测试
(提交代码触发post请求给bus/refresh ----server端接收到请求并发送给Spring Cloud Bus ----Spring Cloud bus接到消息并通知给其它客户端 ----其它客户端接收到通知,请求Server端获取最新配置 ----全部客户端均获取到最新的配置)