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。
@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。
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
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
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
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注解 方法的处理。
//代码中会忽略掉一些日志记录
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
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();
}
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,在启动之后做了哪些事情: