• 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)
    

    至于为啥会有这么多坑

  • 相关阅读:
    Oracle创建自增字段方法-ORACLE SEQUENCE的简介
    iOS项目开发实战——使用Xcode6设计自己定义控件与图形
    准备开源用javascript写Tomcat下的WebApp的项目
    Codeforces Round #256 (Div. 2) B. Suffix Structures
    静默方式安装10g数据库软件+升级patch+手工建库
    oracle 数据库开发面试题
    待机异常篇
    HTTP状态码(HTTP Status Code)
    POJ3126——Prime Path
    RHEL7 -- 通过gerp使用正则表达式
  • 原文地址:https://www.cnblogs.com/zby9527/p/14363965.html
Copyright © 2020-2023  润新知