nacos-spring-project 的注解配置

Posted 偶尔发呆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nacos-spring-project 的注解配置相关的知识,希望对你有一定的参考价值。

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;
}

 

以上是关于nacos-spring-project 的注解配置的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记——Spring中的注解;Spring中装配对象的注解;使用注解配置对象中属性

SpringMvc mybatis 基于注解式的事务管理怎么配

spring在IoC容器中装配Bean详解

@Enable*注解的原理

常用的SSH注解标签

Spring Boot中的注解