这里写自定义目录标题
spring项目集成nacos问题解决
记录传统spring项目集成nacos的过程中遇到的问题
nacos无法使用本地配置
@Configuration
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "${nacos.server-addr}", namespace = "${nacos.namespace}"))
@NacosPropertySource(dataId = "test.properties", groupId = "${nacos.groupId}",autoRefreshed = true)
public class NacosConfiguration {
}
如上代码,在配置中想使用本地配置行不通,直接启动失败,查看源码,
通过import一个BeanDefinitionRegistar来注册nacos的相关类,在执行这个类时会
连接nacos,此时spring还未加载本地配置,所以通过占位符获取配置项无效。
解决方案
在注册nacos的类之前,先加载本地配置到系统变量,启动代码添加如下:
Properties pro = new Properties();
InputStream inStream = new BufferedInputStream(new FileInputStream("config/nacos.properties"));
pro.load(inStream);
System.setProperty("nacos.server-addr", pro.getProperty("nacos.server-addr"));
System.setProperty("nacos.namespace", pro.getProperty("nacos.namespace"));
System.setProperty("nacos.groupId", pro.getProperty("nacos.groupId"));
web项目的话,则需要添加启动监听器,在web.xml添加:
<listener>
<listener-class>com.alibaba.nacos.samples.spring.InitSystemPropertiesListener</listener-class>
</listener>
package com.alibaba.nacos.samples.spring;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* custom ServletContextListener,init nacos confg item to the System Property if you want
* use the properties files to config the nacos , but not want use the JVM -D params in
* your start shell
*/
public class InitSystemPropertiesListener implements ServletContextListener {
/**
* the nacos server address
*/
private static final String SERVER_ADDRESS_PROPERTY_NAME = "nacos.server-addr";
/**
* the nacos namespace
*/
private static final String NAMESPACE_PROPERTY_NAME = "nacos.config.namespace";
/**
* the ignoreResourceNotFound property for propertySourcesPlaceholderConfigurer
*/
public static final String IGNORE_RESOURCE_NOT_FOUND = "ignoreResourceNotFound";
/**
* the ignoreUnresolvablePlaceholders property for
* propertySourcesPlaceholderConfigurer
*/
public static final String IGNORE_UNRESOLVABLE_PLACEHOLDERS = "ignoreUnresolvablePlaceholders";
private static final Logger logger = LoggerFactory
.getLogger(InitSystemPropertiesListener.class);
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
logger.info("InitSystemPropertiesListener#contextInitialized,开始准备初始化系统变量");
InputStream input = null;
try {
input = servletContextEvent.getServletContext()
.getResourceAsStream("/WEB-INF/config.properties");
Properties p = new Properties();
p.load(input);
String nacosServer = p.getProperty(SERVER_ADDRESS_PROPERTY_NAME);
String namespace = p.getProperty(NAMESPACE_PROPERTY_NAME);
String ignoreResourceNotFound = p.getProperty(IGNORE_RESOURCE_NOT_FOUND);
logger.info("nacosServer:{},namespace:{}", nacosServer, namespace);
if (StringUtils.isNotEmpty(nacosServer) && !System.getProperties()
.containsKey(SERVER_ADDRESS_PROPERTY_NAME)) {
System.setProperty(SERVER_ADDRESS_PROPERTY_NAME, nacosServer);
}
if (StringUtils.isNotEmpty(namespace)
&& !System.getProperties().containsKey(NAMESPACE_PROPERTY_NAME)) {
System.setProperty(NAMESPACE_PROPERTY_NAME, namespace);
}
if (StringUtils.isNotEmpty(ignoreResourceNotFound)
&& !System.getProperties().containsKey(IGNORE_RESOURCE_NOT_FOUND)) {
System.setProperty(IGNORE_RESOURCE_NOT_FOUND, ignoreResourceNotFound);
}
if (StringUtils.isNotEmpty(ignoreUnresolvablePlaceholders) && !System
.getProperties().containsKey(IGNORE_UNRESOLVABLE_PLACEHOLDERS)) {
System.setProperty(IGNORE_UNRESOLVABLE_PLACEHOLDERS,
ignoreUnresolvablePlaceholders);
}
}
catch (Exception e) {
logger.error("初始化系统变量失败", e);
}
finally {
if (input != null) {
try {
input.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
无法解析占位符
上面问题解决后,项目不再报连接不上nacos,但配置的
@NacosValue("${useLocalCache}")
会报异常
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'useLocalCache' in string value "${useLocalCache}"
查询并比较本地配置文件发现,是因为本地配置文件中配置了
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:all-config.properties</value>
<value>classpath:all-fdfs.properties</value>
</list>
</property>
</bean>
Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个org.springframework.beans.factory.config.PropertyPlaceholderConfigurer的Bean就会停止对剩余PropertyPlaceholderConfigurer的扫描(Spring 3.1已经使用PropertySourcesPlaceholderConfigurer替代PropertyPlaceholderConfigurer了)。
而这个基于命名空间的配置,其实内部就是创建一个PropertyPlaceholderConfigurer Bean而已。换句话说,即Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer,其余的会被Spring忽略掉。
解决方案
忽略不能识别的占位符即可。
加上这个就可以了:
<property name="ignoreUnresolvablePlaceholders" value="true" />
经过以上问题的解决,spring项目即可正常启动并运行。