spring 项目可以通过注解方式使用 nacos-client
使用示例
@Configuration @EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) @NacosPropertySource(dataId = "example", autoRefreshed = true) public class NacosConfiguration { }
加 @Configuration 注解,spring 容器会创建一个对象放到容器中
通过 @EnableNacosConfig 注解,import NacosConfigBeanDefinitionRegistrar,注册 bean
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(NacosConfigBeanDefinitionRegistrar.class) public @interface EnableNacosConfig { NacosProperties globalProperties() default @NacosProperties( endpoint = ENDPOINT_PLACEHOLDER, namespace = NAMESPACE_PLACEHOLDER, accessKey = ACCESS_KEY_PLACEHOLDER, secretKey = SECRET_KEY_PLACEHOLDER, serverAddr = SERVER_ADDR_PLACEHOLDER, contextPath = CONTEXT_PATH_PLACEHOLDER, clusterName = CLUSTER_NAME_PLACEHOLDER, encode = ENCODE_PLACEHOLDER, configLongPollTimeout = CONFIG_LONG_POLL_TIMEOUT_PLACEHOLDER, configRetryTime = CONFIG_RETRY_TIME_PLACEHOLDER, maxRetry = MAX_RETRY_PLACEHOLDER ); }
重点在 NacosConfigBeanDefinitionRegistrar,注册一些简单 bean 和 post processor 的 bean,例如 NacosPropertySourcePostProcessor,
NacosPropertySourcePostProcessor 它扫描容器中的 bean,如果有 @NacosPropertySource 注解,则解析出注解中的属性,并从 nacos server 拉取配置,并保存到 environment 中
// com.alibaba.nacos.spring.context.annotation.config.NacosConfigBeanDefinitionRegistrar#registerBeanDefinitions public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(EnableNacosConfig.class.getName())); // Register Global Nacos Properties Bean registerGlobalNacosProperties(attributes, registry, environment, CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME); // Register Nacos Common Beans registerNacosCommonBeans(registry); // Register Nacos Config Beans registerNacosConfigBeans(registry, environment); // Invoke NacosPropertySourcePostProcessor immediately // in order to enhance the precedence of @NacosPropertySource process invokeNacosPropertySourcePostProcessor(beanFactory); } 直接调用 NacosPropertySourcePostProcessor // com.alibaba.nacos.spring.util.NacosBeanUtils#invokeNacosPropertySourcePostProcessor public static void invokeNacosPropertySourcePostProcessor(BeanFactory beanFactory) { NacosPropertySourcePostProcessor postProcessor = beanFactory.getBean(NacosPropertySourcePostProcessor.BEAN_NAME, NacosPropertySourcePostProcessor.class); postProcessor.postProcessBeanFactory((ConfigurableListableBeanFactory) beanFactory); } // com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#postProcessBeanFactory public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] abstractNacosPropertySourceBuilderBeanNames = BeanUtils.getBeanNames(beanFactory, AbstractNacosPropertySourceBuilder.class); this.nacosPropertySourceBuilders = new ArrayList<AbstractNacosPropertySourceBuilder>( abstractNacosPropertySourceBuilderBeanNames.length); for (String beanName : abstractNacosPropertySourceBuilderBeanNames) { // 设置 NacosPropertySource 的 builder this.nacosPropertySourceBuilders.add( beanFactory.getBean(beanName, AbstractNacosPropertySourceBuilder.class)); } // 设置 ConfigService 的 builder this.configServiceBeanBuilder = getConfigServiceBeanBuilder(beanFactory); String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { processPropertySource(beanName, beanFactory); } } // com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#processPropertySource private void processPropertySource(String beanName, ConfigurableListableBeanFactory beanFactory) { if (processedBeanNames.contains(beanName)) { return; } BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); // 拉取配置 // Build multiple instance if possible List<NacosPropertySource> nacosPropertySources = buildNacosPropertySources(beanName, beanDefinition); // Add Orderly for (NacosPropertySource nacosPropertySource : nacosPropertySources) { // 加入到 environment 中 addNacosPropertySource(nacosPropertySource); addListenerIfAutoRefreshed(nacosPropertySource); } processedBeanNames.add(beanName); } 对 bean 上的注解进行筛选 // com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#buildNacosPropertySources private List<NacosPropertySource> buildNacosPropertySources(String beanName, BeanDefinition beanDefinition) { for (AbstractNacosPropertySourceBuilder builder : nacosPropertySourceBuilders) { if (builder.supports(beanDefinition)) { return builder.build(beanName, beanDefinition); } } return Collections.emptyList(); } 真正拉取配置 // com.alibaba.nacos.spring.core.env.AbstractNacosPropertySourceBuilder#build public List<NacosPropertySource> build(String beanName, T beanDefinition) { Map<String, Object>[] attributesArray = resolveRuntimeAttributesArray(beanDefinition, globalNacosProperties); int size = attributesArray == null ? 0 : attributesArray.length; if (size == 0) { return Collections.emptyList(); } List<NacosPropertySource> nacosPropertySources = new ArrayList<NacosPropertySource>(size); for (int i = 0; i < size; i++) { Map<String, Object> attributes = attributesArray[i]; if (!CollectionUtils.isEmpty(attributes)) { NacosPropertySource nacosPropertySource = doBuild(beanName, beanDefinition, attributesArray[i]); NacosConfigMetadataEvent metadataEvent = createMetaEvent(nacosPropertySource, beanDefinition); initMetadataEvent(nacosPropertySource, beanDefinition, metadataEvent); publishMetadataEvent(metadataEvent); nacosPropertySources.add(nacosPropertySource); } } return nacosPropertySources; } 解析 NacosPropertySources 和 NacosPropertySource 注解上的属性 // com.alibaba.nacos.spring.core.env.AnnotationNacosPropertySourceBuilder#getAnnotationAttributesList private List<Map<String, Object>> getAnnotationAttributesList( AnnotationMetadata metadata, String annotationType) { List<Map<String, Object>> annotationAttributesList = new LinkedList<Map<String, Object>>(); if (NacosPropertySources.class.getName().equals(annotationType)) { Map<String, Object> annotationAttributes = metadata .getAnnotationAttributes(annotationType); if (annotationAttributes != null) { annotationAttributesList.addAll(Arrays.asList( (Map<String, Object>[]) annotationAttributes.get("value"))); } } else if (com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource.class .getName().equals(annotationType)) { annotationAttributesList .add(metadata.getAnnotationAttributes(annotationType)); } return annotationAttributesList; } // com.alibaba.nacos.spring.core.env.AbstractNacosPropertySourceBuilder#doBuild protected NacosPropertySource doBuild(String beanName, T beanDefinition, Map<String, Object> runtimeAttributes) { // Get annotation metadata String name = (String) runtimeAttributes.get(NAME_ATTRIBUTE_NAME); String dataId = (String) runtimeAttributes.get(DATA_ID_ATTRIBUTE_NAME); String groupId = (String) runtimeAttributes.get(GROUP_ID_ATTRIBUTE_NAME); Map<String, Object> nacosPropertiesAttributes = (Map<String, Object>) runtimeAttributes.get(PROPERTIES_ATTRIBUTE_NAME); Properties nacosProperties = resolveProperties(nacosPropertiesAttributes, environment, globalNacosProperties); String nacosConfig = nacosConfigLoader.load(dataId, groupId, nacosProperties); if (!StringUtils.hasText(nacosConfig)) { if (logger.isWarnEnabled()) { logger.warn(format("There is no content for NacosPropertySource from dataId[%s] , groupId[%s] , properties[%s].", dataId, groupId, valueOf(nacosPropertiesAttributes))); } } if (!StringUtils.hasText(name)) { name = buildDefaultPropertySourceName(dataId, groupId, nacosProperties); } NacosPropertySource nacosPropertySource = new NacosPropertySource(name, nacosConfig); nacosPropertySource.setBeanName(beanName); String beanClassName = beanDefinition.getBeanClassName(); if (StringUtils.hasText(beanClassName)) { nacosPropertySource.setBeanType(resolveClassName(beanClassName, classLoader)); } nacosPropertySource.setGroupId(groupId); nacosPropertySource.setDataId(dataId); nacosPropertySource.setProperties(nacosProperties); initNacosPropertySource(nacosPropertySource, beanDefinition, runtimeAttributes); return nacosPropertySource; } // com.alibaba.nacos.spring.util.config.NacosConfigLoader#load(java.lang.String, java.lang.String, java.util.Properties) public String load(String dataId, String groupId, Properties nacosProperties) throws RuntimeException { try { configService = nacosServiceFactory != null ? nacosServiceFactory.createConfigService(nacosProperties) : NacosFactory.createConfigService(nacosProperties); } catch (NacosException e) { throw new RuntimeException("ConfigService can't be created with dataId :" + dataId + " , groupId : " + groupId + " , properties : " + nacosProperties , e); } return NacosUtils.getContent(configService, dataId, groupId); } // com.alibaba.nacos.spring.util.NacosUtils#getContent public static String getContent(ConfigService configService, String dataId, String groupId) { String content = null; try { content = configService.getConfig(dataId, groupId, DEFAULT_TIMEOUT); } catch (NacosException e) { if (logger.isErrorEnabled()) { logger.error("Can't get content from dataId : " + dataId + " , groupId : " + groupId, e); } } return content; }