我一直在尝试掌握位于此处的Spring Boot配置服务器:https://github.com/spring-cloud/spring-cloud-config,阅读了更详细的文档后,我已经成功解决了大部分问题。但是,我还需要编写一个额外的类来实现基于文件的PropertySourceLocator。
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.config.client;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
/**
* @author Al Dispennette
*
*/
@ConfigurationProperties("spring.cloud.config")
public class ConfigServiceFilePropertySourceLocator implements PropertySourceLocator {
private Logger logger = LoggerFactory.getLogger(ConfigServiceFilePropertySourceLocator.class);
private String env = "default";
@Value("${spring.application.name:'application'}")
private String name;
private String label = name;
private String basedir = System.getProperty("user.home");
@Override
public PropertySource<?> locate() {
try {
return getPropertySource();
} catch (IOException e) {
logger.error("An error ocurred while loading the properties.",e);
}
return null;
}
/**
* @throws IOException
*/
private PropertySource getPropertySource() throws IOException {
Properties source = new Properties();
Path path = Paths.get(getUri());
if(Files.isDirectory(path)){
Iterator<Path> itr = Files.newDirectoryStream(path).iterator();
String fileName = null!=label||StringUtils.hasText(label)?label:name+".properties";
logger.info("Searching for {}",fileName);
while(itr.hasNext()){
Path tmpPath = itr.next();
if(tmpPath.getFileName().getName(0).toString().equals(fileName)){
logger.info("Found file: {}",fileName);
source.load(Files.newInputStream(tmpPath));
}
}
}
return new PropertiesPropertySource("configService",source);
}
public String getUri() {
StringBuilder bldr = new StringBuilder(basedir)
.append(File.separator)
.append(env)
.append(File.separator)
.append(name);
logger.info("loading properties directory: {}",bldr.toString());
return bldr.toString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEnv() {
return env;
}
public void setEnv(String env) {
this.env = env;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getBasedir() {
return basedir;
}
public void setBasedir(String basedir) {
this.basedir = basedir;
}
}
然后我将这段代码添加到 ConfigServiceBootstrapConfiguration.java 文件中。
@Bean
public PropertySourceLocator configServiceFilePropertySource(
ConfigurableEnvironment environment) {
ConfigServiceFilePropertySourceLocator locator = new ConfigServiceFilePropertySourceLocator();
String[] profiles = environment.getActiveProfiles();
if (profiles.length==0) {
profiles = environment.getDefaultProfiles();
}
locator.setEnv(StringUtils.arrayToCommaDelimitedString(profiles));
return locator;
}
最终,这样做达到了我想要的效果。 现在我很好奇这是否是我应该做的,或者我还有什么遗漏,而且这已经得到处理,只是我错过了它。
*****为满足Dave的要求编辑的信息*****
如果我删除文件属性源加载器并更新bootstrap.yml,
uri: file://${user.home}/resources
这个示例应用程序在启动时会抛出以下错误:
ConfigServiceBootstrapConfiguration : Could not locate PropertySource: Object of class [sun.net.www.protocol.file.FileURLConnection] must be an instance of class java.net.HttpURLConnection
这就是我认为额外的类需要的原因。至于测试用例,我想你所说的是SpringApplicationEnvironmentRepositoryTests.java,我同意创建环境,但整个应用程序在uri协议为'file'时似乎并没有按预期运行。
******附加编辑*******
我对这个工作方式的理解是这样的: 示例项目依赖于spring-cloud-config-client构件,因此具有spring-cloud-config-server构件的传递依赖项。 客户端构件中的ConfigServiceBootstrapConfiguration.java创建了一个类型为ConfigServicePropertySourceLocator的属性源定位器bean。 config client构件中的ConfigServicePropertySourceLocator.java具有@ConfigurationProperties("spring.cloud.config")注释,并且该类中存在属性uri,因此在bootstrap.yml文件中设置了spring.cloud.config.uri。
我认为这得到了quickstart.adoc中以下语句的支持:
当它运行时,如果默认本地配置服务器在端口8888上运行,则会从中获取外部配置。要修改启动行为,可以使用
bootstrap.properties
(类似于application.properties
但用于应用程序上下文的引导阶段)来更改配置服务器的位置,例如:---- spring.cloud.config.uri: http://myconfigserver.com
此时,某种方式正在使用JGitEnvironmentRepository bean并寻找与github的连接。 我假设由于ConfigServicePropertySourceLocator中设置了uri属性,因此任何有效的uri协议都可以用于指向位置。 这就是为什么我使用'file://'协议,认为服务器会选择NativeEnvironmentRepository。
所以在这一点上,我确定我要么漏掉了某些步骤,要么需要添加文件系统属性源定位器。
希望这清楚一点。
完整堆栈:
java.lang.IllegalArgumentException: Object of class [sun.net.www.protocol.file.FileURLConnection] must be an instance of class java.net.HttpURLConnection
at org.springframework.util.Assert.isInstanceOf(Assert.java:339)
at org.springframework.util.Assert.isInstanceOf(Assert.java:319)
at org.springframework.http.client.SimpleClientHttpRequestFactory.openConnection(SimpleClientHttpRequestFactory.java:182)
at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:140)
at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:76)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:541)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:506)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:448)
at org.springframework.cloud.config.client.ConfigServicePropertySourceLocator.locate(ConfigServicePropertySourceLocator.java:68)
at org.springframework.cloud.bootstrap.config.ConfigServiceBootstrapConfiguration.initialize(ConfigServiceBootstrapConfiguration.java:70)
at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:572)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:952)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:941)
at sample.Application.main(Application.java:20)
uri
。所以你是在配置服务器的“bootstrap.yml”中设置了spring.cloud.config.uri
,还是在客户端中设置?我有点困惑,而且你关于传递依赖项的描述是颠倒的:服务器依赖于客户端,而不是相反,而且spring.cloud.config.uri
是客户端设置,而不是服务器设置。客户端不需要知道git。 - Dave Syerspring.config.location
。即使没有使用Spring Cloud,你也可以做到这一点,但是如果你正在使用Cloud,那么'bootstrap.yml'就可以了。最好的沟通方式是创建一个样例项目,并在某个地方发布它,同时提供完整的说明和/或测试。如果你能做到这一点,那么理解你的意图可能会更容易。 - Dave Syer