nacos-spring使用时@PropertySource注解不生效问题

问题

写了一个nacos整合sprig的demo,依赖

   <dependencies>
   	<dependency>
   		<groupId>com.alibaba.nacos</groupId>
   		<artifactId>nacos-spring-context</artifactId>
   		<version>1.0.0</version>
   	</dependency>
   </dependencies>

代码

package com.zby;

import java.util.concurrent.TimeUnit;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import com.alibaba.nacos.api.annotation.NacosProperties;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.alibaba.nacos.spring.context.annotation.config.EnableNacosConfig;
import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;

@Configuration
@PropertySource("application.properties")
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "${nacos.server.addr}"))
@NacosPropertySource(dataId = "${nacos.server.dataId}", groupId = "${nacos.server.groupId}", autoRefreshed = true, properties = @NacosProperties(namespace = "${nacos.server.namespace}"))
public class Application {

	@NacosValue(value = "${key}", autoRefreshed = true)
	public String value;

	@SuppressWarnings("resource")
	public static void main(String[] args) throws Exception {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
		Application bean = applicationContext.getBean(Application.class);
		while (true) {
			TimeUnit.SECONDS.sleep(1);
			System.out.println(bean.value);
		}
	}

}

本地配置application.properties

nacos.server.addr=127.0.0.1:8848
nacos.server.namespace=dev
nacos.server.groupId=zby
nacos.server.dataId=base.properties

nacos创建dev名称空间,创建配置

此时,运行main函数,报错:

二月 02, 2021 7:59:33 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6433a2: startup date [Tue Feb 02 19:59:33 CST 2021]; root of context hierarchy
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
二月 02, 2021 7:59:34 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@39fb3ab6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,application,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,nacosApplicationContextHolder,annotationNacosInjectedBeanPostProcessor,propertySourcesPlaceholderConfigurer,nacosConfigurationPropertiesBindingPostProcessor,nacosConfigListenerMethodProcessor,nacosPropertySourcePostProcessor,annotationNacosPropertySourceBuilder,nacosValueAnnotationBeanPostProcessor,configServiceBeanBuilder,loggingNacosConfigMetadataEventListener]; root of factory hierarchy
二月 02, 2021 7:59:34 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'application': Injection of @NacosValue dependencies is failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'key' in string value "${key}"
二月 02, 2021 7:59:34 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory destroySingletons
信息: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@39fb3ab6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,application,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,nacosApplicationContextHolder,annotationNacosInjectedBeanPostProcessor,propertySourcesPlaceholderConfigurer,nacosConfigurationPropertiesBindingPostProcessor,nacosConfigListenerMethodProcessor,nacosPropertySourcePostProcessor,annotationNacosPropertySourceBuilder,nacosValueAnnotationBeanPostProcessor,configServiceBeanBuilder,loggingNacosConfigMetadataEventListener]; root of factory hierarchy
二月 02, 2021 7:59:34 下午 com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor destroy
信息: class com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor was destroying!
二月 02, 2021 7:59:34 下午 com.alibaba.nacos.spring.beans.factory.annotation.AnnotationNacosInjectedBeanPostProcessor destroy
信息: class com.alibaba.nacos.spring.beans.factory.annotation.AnnotationNacosInjectedBeanPostProcessor was destroying!
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'application': Injection of @NacosValue dependencies is failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'key' in string value "${key}"
	at com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor.postProcessPropertyValues(AbstractAnnotationBeanPostProcessor.java:183)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1148)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:638)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:942)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:73)
	at com.zby.Application.main(Application.java:26)
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'key' in string value "${key}"
	at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:173)
	at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:125)
	at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:190)
	at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:164)
	at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:167)
	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:764)
	at com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor.doGetInjectedBean(NacosValueAnnotationBeanPostProcessor.java:108)
	at com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor.getInjectedObject(AbstractAnnotationBeanPostProcessor.java:409)
	at com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedFieldElement.inject(AbstractAnnotationBeanPostProcessor.java:626)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor.postProcessPropertyValues(AbstractAnnotationBeanPostProcessor.java:179)
	... 12 more

原因nacos-spring-contex:1.0.0依赖的spring是3.x的,3.x时Spring处理@PropertySource注解是等其他注解处理完了,才会把@PropertySource的配置放进enviroment
org.springframework.context.annotation.ConfigurationClassParser.processPropertySource(AnnotationAttributes)

	/**
	 * Process the given <code>@PropertySource</code> annotation metadata.
	 * @param propertySource metadata for the <code>@PropertySource</code> annotation found
	 * @throws IOException if loading a property source failed
	 */
	private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		String name = propertySource.getString("name");
		String[] locations = propertySource.getStringArray("value");
		int locationCount = locations.length;
		if (locationCount == 0) {
			throw new IllegalArgumentException("At least one @PropertySource(value) location is required");
		}
		for (int i = 0; i < locationCount; i++) {
			locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]);
		}
		ClassLoader classLoader = this.resourceLoader.getClassLoader();
		if (!StringUtils.hasText(name)) {
			for (String location : locations) {
				this.propertySources.push(new ResourcePropertySource(location, classLoader));
			}
		}
		else {
			if (locationCount == 1) {
				this.propertySources.push(new ResourcePropertySource(name, locations[0], classLoader));
			}
			else {
				CompositePropertySource ps = new CompositePropertySource(name);
				for (int i = locations.length - 1; i >= 0; i--) {
					ps.addPropertySource(new ResourcePropertySource(locations[i], classLoader));
				}
				this.propertySources.push(ps);
			}
		}
	}

org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(BeanDefinitionRegistry)

		// Handle any @PropertySource annotations
		Stack<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
		if (!parsedPropertySources.isEmpty()) {
			if (!(this.environment instanceof ConfigurableEnvironment)) {
				logger.warn("Ignoring @PropertySource annotations. " +
						"Reason: Environment must implement ConfigurableEnvironment");
			}
			else {
				MutablePropertySources envPropertySources = ((ConfigurableEnvironment)this.environment).getPropertySources();
				while (!parsedPropertySources.isEmpty()) {
					envPropertySources.addLast(parsedPropertySources.pop());
				}
			}
		}

解决:spring使用5.x,因为5.x会直接一次性处理好@PropertySource
org.springframework.context.annotation.ConfigurationClassParser.processPropertySource(AnnotationAttributes)

	/**
	 * Process the given <code>@PropertySource</code> annotation metadata.
	 * @param propertySource metadata for the <code>@PropertySource</code> annotation found
	 * @throws IOException if loading a property source failed
	 */
	private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		for (String location : locations) {
			try {
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
                                    //!!!!!!这里就加入到Environment了
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
			catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
				// Placeholders not resolvable or resource not found when trying to open it
				if (ignoreResourceNotFound) {
					if (logger.isInfoEnabled()) {
						logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
					}
				}
				else {
					throw ex;
				}
			}
		}
	}

然后,试试自动修改配置,发现没有自动刷新!
原因

@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "${nacos.server.addr}"))
@NacosPropertySource(dataId = "${nacos.server.dataId}", groupId = "${nacos.server.groupId}", autoRefreshed = true, properties = @NacosProperties(namespace = "${nacos.server.namespace}"))

名称空间配置在NacosPropertySource注解里,读配置没问题,自动刷新就不能用了
解决

@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "${nacos.server.addr}", namespace = "${nacos.server.namespace}"))
@NacosPropertySource(dataId = "${nacos.server.dataId}", groupId = "${nacos.server.groupId}", autoRefreshed = true)

至于为啥会有这么多坑

posted @ 2021-02-02 20:14  java拌饭  阅读(8202)  评论(0编辑  收藏  举报