Nacos:与Spring Boot集成源码解读

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration
#org.springframework.context.ApplicationContextInitializer=\
#  com.alibaba.boot.nacos.config.autoconfigure.NacosConfigApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
  com.alibaba.boot.nacos.config.autoconfigure.NacosConfigEnvironmentProcessor

        通过NacosConfigAutoConfiguration自动配置,主要导入NacosConfigBootBeanDefinitionRegistrar和NacosConfigBeanDefinitionRegistrar这两个Bean注册类。

        1、 NacosConfigBootBeanDefinitionRegistrar,如图所示:

在这里插入图片描述
        从结构图可以看出,该类容器启动期间注册其他的Bean。

com.alibaba.boot.nacos.config.autoconfigure.NacosConfigBootBeanDefinitionRegistrar代码片段
@Configuration
public class NacosConfigBootBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
                .rootBeanDefinition(NacosBootConfigurationPropertiesBinder.class);
        //注册NacosBootConfigurationPropertiesBinder,用于@NacosConfigurationProperties注解的属性绑定        
        defaultListableBeanFactory.registerBeanDefinition(NacosBootConfigurationPropertiesBinder.BEAN_NAME, beanDefinitionBuilder.getBeanDefinition());
    }
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}
        2、NacosConfigBeanDefinitionRegistrar,如图所示:

在这里插入图片描述
        从结构图可以看出,该类容器启动期间注册其他的Bean。

com.alibaba.nacos.spring.context.annotation.config.NacosConfigBeanDefinitionRegistrar代码片段
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
	//获取@EnableNacosConfig注解的配置
    AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(EnableNacosConfig.class.getName()));
    //1. 注册全局的Nacos属性bean
    registerGlobalNacosProperties(attributes, registry, environment, CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME);
    //2. 注册Nacos通用的bean
    registerNacosCommonBeans(registry);
    //3. 注册Nacos配置的bean
    registerNacosConfigBeans(registry, environment);
    //4. 为了提高@NacosPropertySource处理的优先级,立即调用NacosPropertySourcePostProcessor
    invokeNacosPropertySourcePostProcessor(beanFactory);
}

        按照上编排的顺序,我们来看看每一步具体都做了什么。

        1) 注册全局Nacos属性的Bean

com.alibaba.nacos.spring.util.NacosBeanUtils#registerGlobalNacosProperties代码片段
public static void registerGlobalNacosProperties(AnnotationAttributes attributes,
                                                     BeanDefinitionRegistry registry,
                                                     PropertyResolver propertyResolver,
                                                     String beanName) {
    if (attributes == null) {
        return; // Compatible with null
    }
    AnnotationAttributes globalPropertiesAttributes = attributes.getAnnotation("globalProperties");
    registerGlobalNacosProperties((Map<?, ?>) globalPropertiesAttributes, registry, propertyResolver,
            beanName);
}

        2) 注册Nacos通用的Bean

com.alibaba.nacos.spring.util.NacosBeanUtils#registerNacosCommonBeans代码片段
public static void registerNacosCommonBeans(BeanDefinitionRegistry registry) {
    //注册NacosApplicationContextHolder bean,其实就是持有ApplicationContext实例的辅助类
    registerNacosApplicationContextHolder(registry);
    //注册AnnotationNacosInjectedBeanPostProcessor bean
    registerAnnotationNacosInjectedBeanPostProcessor(registry);
}

        AnnotationNacosInjectedBeanPostProcessor,通过名称我们可以看出应该是处理@NacosInjected注解的,结构图所示:
在这里插入图片描述

在这里插入代码片

        与Spring @Autowired类似,该类的功能是通过@NacosInjected注解,完成Nacos的服务实例注入到我们自定义的Bean中。

        3) 注册Nacos配置的Bean

com.alibaba.nacos.spring.util.NacosBeanUtils#registerNacosConfigBeans代码片段
public static void registerNacosConfigBeans(BeanDefinitionRegistry registry, Environment environment) {
    //注册PropertySourcesPlaceholderConfigurer Bean
    registerPropertySourcesPlaceholderConfigurer(registry);
	//注册NacosConfigurationPropertiesBindingPostProcessor Bean,处理@NacosConfigurationProperties注解的属性绑定
    registerNacosConfigPropertiesBindingPostProcessor(registry);
	//注册NacosConfigListenerMethodProcessor Bean,处理@NacosConfigListener注解方法的监听
    registerNacosConfigListenerMethodProcessor(registry);
	//注册NacosPropertySourcePostProcessor Bean,处理@NacosPropertySource注解的配置拉取
    registerNacosPropertySourcePostProcessor(registry);
	//注册AnnotationNacosPropertySourceBuilder Bean,处理@NacosPropertySource注解的属性构造
    registerAnnotationNacosPropertySourceBuilder(registry);
	//注册Nacos配置监听的处理器,其实是ExecutorService线程池
    registerNacosConfigListenerExecutor(registry, environment);
	//注册NacosValueAnnotationBeanPostProcessor Bean,处理@NacosValue注解的注入,同时监听Nacos内容配置的变更,完成属性值的变更
    registerNacosValueAnnotationBeanPostProcessor(registry);
	//注册ConfigServiceBeanBuilder Bean,用于根据Nacos的配置创建配置服务实例
    registerConfigServiceBeanBuilder(registry);
	//注册LoggingNacosConfigMetadataEventListener Bean
	//监听Nacos配置绑定事件,记录日志,在NacosConfigurationPropertiesBinder类中会触发该事件
    registerLoggingNacosConfigMetadataEventListener(registry);
}

在这里插入图片描述

在这里插入图片描述
        从上述的结构图中,可以看出NacosConfigListenerMethodProcessor实现了监听器的功能,监听ContextRefreshedEvent(容器刷新事件),完成@NacosConfigListener注解 方法的处理。

com.alibaba.nacos.spring.context.annotation.config.NacosConfigListenerMethodProcessor代码片段
//代码中会忽略掉一些日志记录
private void processBean(final String beanName, final Object bean, final Class<?> beanClass, final ApplicationContext applicationContext) {

    ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
        @Override
        public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
        	//判断拦截的方法是否有@NacosConfigListener注解
            A annotation = AnnotationUtils.getAnnotation(method, annotationType);
            if (annotation != null && isCandidateMethod(bean, beanClass, annotation, method, applicationContext)) {
                processListenerMethod(beanName, bean, beanClass, annotation, method, applicationContext);
            }
        }

    }, new ReflectionUtils.MethodFilter() {
        @Override
        public boolean matches(Method method) {
        	//判断监听方法的合法性(公有、无返回值void)
            return isListenerMethod(method);
        }
    });

}

protected void processListenerMethod(String beanName, final Object bean, Class<?> beanClass,
                                     final NacosConfigListener listener, final Method method,
                                     ApplicationContext applicationContext) {
	//获取Nacos配置的标识
    final String dataId = NacosUtils.readFromEnvironment(listener.dataId(), environment);
    final String groupId = NacosUtils.readFromEnvironment(listener.groupId(), environment);
    final String type = StringUtils.isEmpty(NacosUtils.readTypeFromDataId(dataId)) ? listener.type().getType() : NacosUtils.readTypeFromDataId(dataId);
    long timeout = listener.timeout();
    //根据配置属性,构造连接远程的Nacos配置服务
    ConfigService configService = configServiceBeanBuilder.build(listener.properties());
    try {
    	//为所要监听的配置,添加监听器
        configService.addListener(dataId, groupId, new TimeoutNacosConfigListener(dataId, groupId, timeout) {
            @Override
            protected void onReceived(String config) {
            	//在监听器接收到配置变更时,会把变更内容根据监听方法的第一个参数类型进行转换
                Class<?> targetType = method.getParameterTypes()[0];
                NacosConfigConverter configConverter = determineNacosConfigConverter(targetType, listener, type);
                Object parameterValue = configConverter.convert(config);
                // Execute target method
                ReflectionUtils.invokeMethod(method, bean, parameterValue);
            }
        });
    } catch (NacosException e) {
    }
	//发布Nacos配置解析完事件,进行日志记录
    publishMetadataEvent(beanName, bean, beanClass, dataId, groupId, listener, method);
}

在这里插入图片描述

        4) 为了提高@NacosPropertySource处理的优先级,立即调用NacosPropertySourcePostProcessor

在这里插入图片描述

com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor代码片段
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	//获取@NacosPropertySource注解属性的构造器
    String[] abstractNacosPropertySourceBuilderBeanNames = BeanUtils.getBeanNames(beanFactory,
            AbstractNacosPropertySourceBuilder.class);
    this.nacosPropertySourceBuilders = new ArrayList<AbstractNacosPropertySourceBuilder>(
            abstractNacosPropertySourceBuilderBeanNames.length);
    for (String beanName : abstractNacosPropertySourceBuilderBeanNames) {
        this.nacosPropertySourceBuilders.add(
                beanFactory.getBean(beanName, AbstractNacosPropertySourceBuilder.class));
    }
    NacosPropertySourcePostProcessor.beanFactory = beanFactory;
    //获取Nacos配置服务的构造器
    this.configServiceBeanBuilder = getConfigServiceBeanBuilder(beanFactory);
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
    	//处理属性
        processPropertySource(beanName, beanFactory);
    }
}

private void processPropertySource(String beanName, ConfigurableListableBeanFactory beanFactory) {
    if (processedBeanNames.contains(beanName)) {
        return;
    }
    BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
    //构造属性实例,会使用到容器已经注册的AnnotationNacosPropertySourceBuilder Bean
    List<NacosPropertySource> nacosPropertySources = buildNacosPropertySources(beanName, beanDefinition);
    //排序
    for (NacosPropertySource nacosPropertySource : nacosPropertySources) {
        addNacosPropertySource(nacosPropertySource);
        Properties properties = configServiceBeanBuilder.resolveProperties(nacosPropertySource.getAttributesMetadata());
        //如果属性自动刷新,则添加监听器处理
        addListenerIfAutoRefreshed(nacosPropertySource, properties, environment);
    }
    processedBeanNames.add(beanName);
}

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.AnnotationNacosPropertySourceBuilder代码片段
public List<NacosPropertySource> build(String beanName, T beanDefinition) {
	//获取@NacosPropertySource注解属性集合
    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)) {
        	//从远程Nacos拉取配置,绑定
            NacosPropertySource nacosPropertySource = doBuild(beanName, beanDefinition, attributesArray[i]);
            //创建事件
            NacosConfigMetadataEvent metadataEvent = createMetaEvent(nacosPropertySource, beanDefinition);
            //初始化事件
            initMetadataEvent(nacosPropertySource, beanDefinition, metadataEvent);
            //发布事件
            publishMetadataEvent(metadataEvent);
            nacosPropertySources.add(nacosPropertySource);
        }
    }
    return nacosPropertySources;
}

三、总结

        通过上述的分析,我们总结下Nacos集成Spring Boot,在启动之后做了哪些事情:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值