在项目上做开发时Apollo客户端获取配置信息报错,我只是个小白网上,也找不到答案,只好自己一点点看下去了

错误:

Error creating bean with name ‘entity’: Injection of autowired
dependencies failed; nested exception is java.lang.IllegalArgumentException: key can’t be empty

现在我们在本地环境试试会不会出现同样的问题,代码如下:
相关配置:


apollo-client版本 1.5


本篇文章比较水,会慢慢补充完善,希望各位能留下宝贵建议与意见,或者提出一些问题共同讨论。

bootstrap.properties配置文件

app.id=prometheus
apollo.meta=http://127.0.0.1:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application,AlertConfig,AlertRules
env=dev

获取apollo 配置的bean

@Component
public class Entity implements Serializable {
    @Value("${rulesPath}")
    private String rulesPath;

    public String getRulesPath() {
        return rulesPath;
    }
    public void setRulesPath(String rulesPath) {
        this.rulesPath = rulesPath;
    }
}

@Autowired
private Entity entity;

@PostConstruct
private void printLog(){
  System.out.println(entity.getRulesPath());
}

在这里插入图片描述
结果:
在这里插入图片描述我们试着换一下namespace的顺序

apollo.bootstrap.namespaces=AlertConfig,application,AlertRules

在这里插入图片描述

再换一下:

apollo.bootstrap.namespaces=AlertRules,AlertConfig,application

在这里插入图片描述
我们试了一下并没有出现同样的问题,那么问题到底出在了哪里?
从实验结果中发现,客户端获取到的值和Apollo 的 namespace有关,获取配置中第一个namespace中配置的数据,

那么问题到底出在哪?
我们尝试将@Value("${}")设置成这样

@Component
@EnableApolloConfig("AlertConfig")
public class Entity implements Serializable {
//    @Value("${rulesPath}")
    @Value("${}")
    private String rulesPath;

    public String getRulesPath() {
        return rulesPath;
    }
    public void setRulesPath(String rulesPath) {
        this.rulesPath = rulesPath;
    }

在这里插入图片描述

似乎我们遗漏了一个细节:测试环境中我们的配置 value中有个特殊的东西 ${}
在这里插入图片描述

终于发现答案了,原来是@Value对 SpEL表达式进行了解析,先获取到了apollo配置中 rulesPath 对应的值,然后又对值里边的SpEL表达式进行了解析,所以出现了key can’t be empty的错误。

事情到这里就结束了吗?

现在我知道是哪里出了问题,我只需要将数据中的${}删除掉就好了,但是这个锅到底由谁来背呢?

在这里插入图片描述

#app.id=prometheus
## set apollo meta server address, adjust to actual address if necessary
#apollo.meta=http://192.168.11.11:8080
#apollo.bootstrap.enabled=true
#apollo.bootstrap.namespaces=application,AlertRules,AlertConfig
#env=dev
@Slf4j
@SpringBootApplication
public class DemoApplication /*implements CommandLineRunner*/ {
@Component
public class Entity implements Serializable {
@Component
@ConfigurationProperties(value = "test")
public class config {

    @Value("${key:123}")
    private String key;

我们不用apollo了,直接在application.yml 配置文件里写个简单的参数行不行?
在这里插入图片描述
然后启动一下:
在这里插入图片描述

那么 如果我们在表达式里再写一些key还可以解析吗?

前方高能警告!!!!

在这里插入图片描述
O(∩_∩)O 发生了什么!它还可以解析SpEL里的key 但是如果是空的也是没有关系的
在这里插入图片描述

距离真相越来越近了

**20201028更新:**那么我们来看看是哪些骚操作导致这个错误

#app.id=prometheus  #appid
## set apollo meta server address, adjust to actual address if necessary
#apollo.meta=http://192.168.11.11:8080   #ip
#apollo.bootstrap.enabled=true
#apollo.bootstrap.namespaces=application,AlertRules,AlertConfig
#env=dev  #指定环境

看一下获取el 表达式的过程 Spring 源码如下:

org.springframework.util.PropertyPlaceholderHelper#parseStringValue
	protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {

		int startIndex = value.indexOf(this.placeholderPrefix);
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				// Recursive invocation, parsing placeholders contained in the placeholder key.
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				// Now obtain the value for the fully resolved key...
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				if (propVal == null && this.valueSeparator != null) {
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value \"" + value + "\"");
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}
		return result.toString();
	}

while 循环里会解析获取到的值是否包含 ${} ,一直到不包含为止。

!!!所以这个问题是由于自己对Spring 的了解不够深刻造成的,因此,这个锅我背了

实际上就是@value使用的spel表达式会解析获取到的的内容里的特殊符号,之前是自己想多了,这是spel表达式的问题,如果apollo 配置的value 中包含有特殊的符号之类还是建议用 @ApolloConfig(“namespace”) 获取比较安全

从上边的测试中我们发现同样是使用@value注解,但是由于数据源的不同,导致产生不同的结果.

实验结果:
项目中引入配置文件并且配置文件中配置正确时,项目能够加载,项目中并未使用@EnableApolloConfig注解,直接使用@ApolloConfig注解也可获取。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
加载顺序问题:

apollo.bootstrap.enabled:在应用启动阶段,向Spring容器注入被托管的application.properties文件的配置信息。

apollo.bootstrap.enabled=true时打印的结果为配置中心的值
apollo.bootstrap.enabled=false时打印的结果为本地配置文件中的值

20201103更新:这个问题还是记录下来吧;
在另一台电脑开的虚拟机,使用vm网络配置做的转发,在实际使用的过程中会遇到开启Apollo客户端无法连接的情况,但这个原因是由于Apollo的工作模式决定的,因此使用nginx做了反向代理并修改Apollo服务端配置;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dave_Fong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值