在spring cloud alibaba中,配置中心是关键性的组件,能够提供便捷的配置管理以及更改配置后无需启动服务,就能够实现配置的自动更新。当然自动更新的操作并不是由nacos来完成,但是涉及以及配置更新通知,则是由nacos来实现的。这篇文章主要介绍nacos配置加载实现。
在spring boot 环境配置加载原理中,我们介绍了spring boot默认配置文件的加载逻辑和实现,在主要的配置文件加载中,并没有对nacos相关的配置进行加载,而是放在了容器的初始化准备阶段。
1. BootstrapApplicationListener
这里重提了该类,因为该类在spring-cloud配置的依赖加入后,对spring-boot的环境配置起了很重要的作用,因为在该类的实现中,需要创建一个新的ApplicationContext的容器,然后对容器进行实例化的操作,这样的ApplicationContext的容器实际上是作为parent的存在,然后在spring-cloud-alibaba版本中,nacos的配置加载则与该类有很重要的联系。
在 创建ApplicationContext的逻辑中,会指定sources信息,该sources信息主要是Configuration类型或者组件类,具体源码如下:
builder.sources(BootstrapImportSelectorConfiguration.class);
final ConfigurableApplicationContext context = builder.run();
// gh-214 using spring.application.name=bootstrap to set the context id via
// ``ContextIdApplicationContextInitializer`` prevents apps from getting the actual
// spring.application.name
// during the bootstrap phase.
context.setId("bootstrap");
// Make the bootstrap context a parent of the app context
addAncestorInitializer(application, context);
// It only has properties in it now that we don't want in the parent so remove
// it (and it will be added back later)
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
通过源码可以知道,在资源中加入了BootstrapImportSelectorConfiguration的类型,而这个类型则是作为初始化nacos配置加载的入口。
2. SpringApplication
createApplicationContext()
创建容器的方法这里需要作为重点来说明一下,因为在创建ApplicationContext容器的时候,会有许多内置的对象在初始化的时候就被放入到了ApplicationContext容器中
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
这里将相关的代码放到了一起,便于查看
创建ApplicationContext对象是通过工厂模式进行创建,这个工厂对象是在程序代码中预定义的一个内部类。具体如下:
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
// 通过SPI价值加载ApplicationContextFactory对象,然后通过工厂类进行ApplicationContext的创建
// 如果创建成功,则直接返回ApplicationContext, 否则就使用AnnotationConfigApplicationContext对象代替
for (ApplicationContextFactory candidate : SpringFactoriesLoader
.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
ConfigurableApplicationContext context = candidate.create(webApplicationType);
if (context != null) {
return context;
}
}
return new AnnotationConfigApplicationContext();
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
在这段定义中,包含了两层的意思:
- 通过SPI方式可以创建ApplicationContext并且可以阻断后续的工厂类创建ApplicationContext.
- 其次如果在不通过工厂类的方式创建ApplicationContext容器时,默认使用AnnotationConfigApplicationContext来实现。
这里有个隐藏信息需要注意:
在引入spring-cloud-bootstrap包信息的时候,我们实际上是创建了一个parent的容器来实现bootstrap层面的容器初始化,这个时候的初始化其实与应用类型时无关的,因为在bootstrap的时候,默认是将application设置为
none的,这里就会显的很重要,因为在bootstrap初始化的时候,如果本身有web的内容,是不会导致web容器的初始化行为。
AnnotatedBeanDefinitionReader
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 注入处理器信息
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
在构造函数中,则包含了对容器一些必要类型的注入操作,
AnnotationConfigUtils
registerAnnotationConfigProcessors()
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 获取BeanFactory对象
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
// 设置AnnotationAwareOrderComparator
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
// 设置 ContextAnnotationAutowireCandidateResolver
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 判断是否包含了org.springframework.context.annotation.internalConfigurationAnnotationProcessor的bean定义对象,如果没有包含
// 则新增ConfigurationClassPostProcessor为定义对象
// 这个类就很重要了,他是加载Configuration实现的重要类型
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 判断是否包含了org.springframework.context.annotation.internalAutowiredAnnotationProcessor的bean定义对象
// 如果没有,则加入AutowiredAnnotationBeanPostProcessor定义
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
// 这是对JSR-250的支持,判断是否加入了org.springframework.context.annotation.internalCommonAnnotationProcessor的定义
// 如果没有,则加入CommonAnnotationBeanPostProcessor定义
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// org.springframework.context.event.internalEventListenerProcessor的定义
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
// org.springframework.context.event.internalEventListenerFactory的定义
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
在以上的过程中,就做好了容器刷新的初始准备,接下来就是刷新,然后通过配置加载各种配置定义信息。
prepareContext()
[dangerbox title=”标题内容”][/dangerbox]
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境配置
context.setEnvironment(environment);
// 处理容器
postProcessApplicationContext(context);
// 执行ApplicationContextInitializer中的intialize方法,处理必要参数
applyInitializers(context);
// 广播ApplicationContextInitializedEvent时间
listeners.contextPrepared(context);
// 关闭BootstrapContext容器,并准备ApplicationContext
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 获取BeanFactory对象
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 注册命令行参数对象
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 打印banner信息
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置allowBeanFefinitionOverriding参数
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
// 是否懒加载,如果支持懒加载,则LazyInitializationBeanFactoryPostProcessor实例
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 新增PropertySourceOrderingBeanFactoryPostProcessor实例
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// 获取需要加载的资源列表,也即是启动源类型
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载资源
load(context, sources.toArray(new Object[0]));
// 发送事件ApplicationPreparedEvent
listeners.contextLoaded(context);
}
准备工作主要是对ApplicaionContext声明周期的管理和操作,其中也设计到了SPI自定义的一些步骤,同时,在不同阶段,都会发送事件通知信息。
load()
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// 创建BeanDefinitionLoader对象,该对象持有启动源信息
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 执行加载操作
loader.load();
}
这里需要注意一下,在引入了spring-cloud-bootstrap依赖的时候,这里加载parent的容器时,这里的源只有一个:
org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration这个类的来源实在parent被创建的时候自动设置进去的。
因此这里的加载,实际上是对BootstrapImportSelectorConfiguration类型的处理。
load()方法处理完成之后,实际上在BeanFactory中就包含了BootstrapImportSelectorConfiguration的定义信息,也就是BeanDefinition信息。
refresh()
在知道了处理nacos配置的入口后,则我们主要关系在执行run()方法的时候,会通过refreshContext()方法刷新容器,具体如下:
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
这里就最终调用了ApplicationContext#refresh()方法,因此我们只需要看对应的方法即可。
3. AbstractApplicationContext
refresh()
因为这篇文章不是为了介绍ApplicationContext的加载过程,因此里面的步骤不会做详细介绍,而是只支出关键的配置步骤
try {
...
// Invoke factory processors registered as beans in the context.
// 执行BeanFactoryPostProcessor实例的方法
invokeBeanFactoryPostProcessors(beanFactory);
...
}
invokeBeanFactoryPostProcessors()
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// 判断beanFactory是否为BeanDefinitionRegistry实例
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 执行BeanFactoryPostProcessor实例,这里主要是对BeanFactory进行后置处理
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 这步骤是为了获取BeanDefinitionRegistryPostProcessor相关的beanName列表
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 遍历BeanDefinitionRegistryPostProcessor名称信息
for (String ppName : postProcessorNames) {
// 判断当前beanName代表的class对象是否为PriortyOrdered的实现类
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 如果条件满足,则通过getBean()方法创建bean,并加入到currentRegistryProcessors集合中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 对BeanDefinitionRegistryPostProcessor实例列表进行优先级排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 执行BeanDefinitionRegistryPostProcessor实例
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// 获取BeanDefinitionRegistryPostProcessor实例列表,并按照Ordered排序,然后执行BeanDefinitionRegistryPostProcessor逻辑
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// 如果BeanDefinitionRegistryPostProcessor没有实现任何的排序规则或者逻辑,则最后执行,优先级最低
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
...
}
4. ConfigurationClassPostProcessor
对于配置类型的加载,主要是通过该类来完成的。
postProcessBeanDefinitionRegistry()
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 获取编号
int registryId = System.identityHashCode(registry);
// 判断是否已经执行了post process
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
// 是否通过工厂类执行过注册
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
// 将当前注册编号加入到registriesPostProcessed列表中
this.registriesPostProcessed.add(registryId);
// 指定bean定义配置
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions()
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取registry中已经注册的BeanDefinition名称列表
String[] candidateNames = registry.getBeanDefinitionNames();
//遍历bean名称
for (String beanName : candidateNames) {
// 获取对应的beanName的类型定义信息
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 判断是否包含属性org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 判断是否与Configuration类的加载逻辑,主要是对@Configuration逻辑的处理
// 若成立,则加入到configCandidates列表中
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果configCandidates为空,则表示没有Configuration类需要处理
if (configCandidates.isEmpty()) {
return;
}
// 根据@Order注解进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
// 这里主要是为了探测自定义bean名称生成策略
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 创建ConfigurationClassParser对象,该对象用于解析@Configuration注解有关的配置类信息
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 开始解析Configuration配置类
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 在parse类中,实现了对Configuration类型的加载,当有了这些Configuration类型之后
// 则需要将Configuration中包含有@Bean这些方法解析为BeanDefinition对象,以便于对这些类型进行初始化
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
// 将已经加载的配置类放到已经解析的列表中
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
// 新增配置类型,则加入到candidates列表中
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
// 将新的参与类复制,再次进行遍历
candidateNames = newCandidateNames;
}
}
// 循环遍历
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
因此经过以上步骤之后,则对应的Configuraion类被加载并且类中的@Bean配置已经注入到了BeanFactory中,这个时候,就设计到初始化Bean的操作
5. ConfigurationClassParser
parse()
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 开始遍历Configuration的类定义信息
for (BeanDefinitionHolder holder : configCandidates) {
// 获取Configuration的BeanDefinition信息
BeanDefinition bd = holder.getBeanDefinition();
try {
// 判断是否为AnnotatedBeanDefinition, 如果是,则执行对应逻辑
// 这里在bootstrap阶段,对应的类型为BootstrapImportSelectorConfiguration,
// 该类因为涉及到注解的使用信息,因此为AnnotatedBeanDefinition定义
// parse()操作会将对应的@Configuration以及@Import等注解解析为可执行的实例
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
这里没有介绍详细的过程,是因为这个属于spring-boot注解加载过程,因此不在这次的讨论范围内,可以参考加载的过程
DeferredImportSelectorHandler
该类是作为ConfigurationClassParser的内部类,该类主要处理@Import相关的配置类的引入逻辑。
public void process() {
// 获取Import列表。当加载source中包含了@Import注解的时候,则不为空
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
// 创建处理@Import逻辑的类实例
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 将DeferredImportSelectorHolder类型注册到deferredImports列表中
deferredImports.forEach(handler::register);
// 处理@Import逻辑
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
DeferredImportSelectorGroupingHandler
public void processGroupImports() {
// 开始遍历DeferredImportSelectorGrouping信息,
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 从grouping中获取Imports列表,这里需要主要关注下getImports()方法
// 因为一个Configuration类可能会引入很多的配置类,然后会遍历去导入
// 其他的配置类,这里也是nacos加载配置扩展的点。
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// 加载其他的配置类,和上面逻辑基本一致
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
通过以上的源码分析,getImports()方法中有加载其他配置类的功能,因此,将主要关注getImports()方法的实现。
DeferredImportSelectorGrouping
getImports()
public Iterable<Group.Entry> getImports() {
// 遍历DeferredImportSelectorHolder,该类中保存中@Import注解中需要保存的类型,分别处理对应的导入逻辑
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 开始处理单个selector导入
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
DefaultDeferredImportSelectorGroup
process()
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
// 通过selector#selectImports()方法获取导入的class名称列表, 并加入到导入列表中
for (String importClassName : selector.selectImports(metadata)) {
this.imports.add(new Entry(metadata, importClassName));
}
}
这里主要通过selector的实现获取导入类的列表,以此完成加载其他配置类的目的。
6. BootstrapImportSelector
在bootstrap阶段,因为当前源为class org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration配置类,该类实际上也是通过@Import注解引入了BootstrapImportSelector类的加载过程,因此这里最终会调用到当前的类中。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 获取类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 这里通过SPI机制,加载BootstrapConfiguration实现类名称列表
// 这里使用类名称而不是类的实例,主要是为了防止相同类的重复加载
List<String> names = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(BootstrapConfiguration.class, classLoader));
// 从spring.cloud.bootstrap.sources中加载需要配置的类列表
names.addAll(Arrays.asList(StringUtils
.commaDelimitedListToStringArray(this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));
List<OrderedAnnotatedElement> elements = new ArrayList<>();
// 遍历并创建OrderedAnnotatedElement类型,并加入到待加入类列表
for (String name : names) {
try {
elements.add(new OrderedAnnotatedElement(this.metadataReaderFactory, name));
}
catch (IOException e) {
continue;
}
}
// 排序
AnnotationAwareOrderComparator.sort(elements);
String[] classNames = elements.stream().map(e -> e.name).toArray(String[]::new);
return classNames;
}
因此通过该方式后,NacosConfigBootstrapConfiguration的配置类就被加入到了内存,紧接着就是对该配置类的处理。
这里需要注意,从spring-cloud的角度来说,扩展类加载配置有两种方式:
- 第一种就是通过SPI的方式进行配置,在
spring.factories中配置org.springframework.cloud.bootstrap.BootstrapConfiguration=即可 - 第二种就是通过
spring.cloud.bootstrap.sources配置需要加载类列表
7. Nacos加载配置到Environment中
通过以上的过程解析,我们明白了nacos是如何将配置放到容器中进行加载和初始化bean的过程。当主备工作完成之后,实际上与nacos有关的配置加载的类定义就已经在BeanFactory中。
SpringApplication#applyInitializers()
再次回到这个类的时候,主要是因为在执行准备阶段,会执行ApplicationContextInitializer初始化实例的初始化方法,其中有一个类org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration该类会初始化所有的PropertySourceLocator实例,并调用locate()方法加载配置数据信息。
NacosPropertySourceLocator
该类就是nacos具体加载配置文件的地方。可以通过该类查看配置加载的源码信息。
loadSharedConfiguration()
加载共享配置信息
private void loadSharedConfiguration(
CompositePropertySource compositePropertySource) {
// 获取spring.cloud.nacos.config.share-configs配置,
// 这里配置也是通过指定前缀和配置名称加载的,因此不做过解释
List<NacosConfigProperties.Config> sharedConfigs = nacosConfigProperties
.getSharedConfigs();
if (!CollectionUtils.isEmpty(sharedConfigs)) {
checkConfiguration(sharedConfigs, "shared-configs");
loadNacosConfiguration(compositePropertySource, sharedConfigs);
}
}
loadExtConfiguration()
spring.cloud.nacos.config.ext-configs配置加载
private void loadExtConfiguration(CompositePropertySource compositePropertySource) {
// 获取ext-configs配置信息,如果配置存在,则加载配置文件
List<NacosConfigProperties.Config> extConfigs = nacosConfigProperties
.getExtensionConfigs();
if (!CollectionUtils.isEmpty(extConfigs)) {
checkConfiguration(extConfigs, "extension-configs");
loadNacosConfiguration(compositePropertySource, extConfigs);
}
}
loadApplicationConfiguration()
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
// 获取文件后缀信息,默认为properties
String fileExtension = properties.getFileExtension();
// 获取分组信息,默认值为DEFAULT_GROUP
String nacosGroup = properties.getGroup();
// 使用默认配置加载配置文件
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,fileExtension, true);
// 加载带有后缀的配置信息,带有后缀的加载信息比默认的配置具有更高的优先级.也就是dataIdPrefix.fileExtension文件名
loadNacosDataIfPresent(compositePropertySource,dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// 如果环境配置中配置了环境相关信息,则加载带有环境信息的配置
// 即加载dataIdPrefix-profile.fileExtension格式的配置,比带有文件后缀的有更高的优先级。
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,fileExtension, true);
}
}
从该方法中,我们可以得知,nacos加载配置文件也分了三个优先级:
- 默认优先级,该优先级则只有
dataIdPrefix信息,优先级最低 - 带有文件扩展名的配置文件,即
dataIdPrefix.fileExtension, 这个配置文件比默认配置有更高的优先级 - 带有profile的文件配置,即
dataIdPrefix-profile.fileExtension, 这种配置具有最高的优先级。
因此最终我们将关注的点放在loadNacosDataIfPresent()方法上。
loadNacosDataIfPresent()
private void loadNacosDataIfPresent(final CompositePropertySource composite,
final String dataId, final String group, String fileExtension,
boolean isRefreshable) {
// 判断是否包含了dataId, 如果不包含,则字节返回
if (null == dataId || dataId.trim().length() < 1) {
return;
}
// 判断是否包含group, 如果不包含,则返回
if (null == group || group.trim().length() < 1) {
return;
}
// 加载NacosPropertySource对象
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
this.addFirstPropertySource(composite, propertySource, false);
}
loadNacosPropertySource()
private NacosPropertySource loadNacosPropertySource(final String dataId,
final String group, String fileExtension, boolean isRefreshable) {
if (NacosContextRefresher.getRefreshCount() != 0) {
if (!isRefreshable) {
return NacosPropertySourceRepository.getNacosPropertySource(dataId,
group);
}
}
return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
isRefreshable);
}
可以看到这里使用到了构造器模式,创建NacosPropertySource对象使用NacosPropertySourceBuilder对象进行创建。
addFirstPropertySource()
private void addFirstPropertySource(final CompositePropertySource composite,
NacosPropertySource nacosPropertySource, boolean ignoreEmpty) {
if (null == nacosPropertySource || null == composite) {
return;
}
if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) {
return;
}
composite.addFirstPropertySource(nacosPropertySource);
}
将nacos配置放到环境配置首部,提升优先级。
NacosPropertySourceBuilder
build()
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
// 加载配置源列表
List<PropertySource<?>> propertySources = loadNacosData(dataId, group,fileExtension);
// 创建NacosPropertySource对象
NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources, group, dataId, new Date(), isRefreshable);
// 整理nacosPropertySource
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}
loadNacosData()
private List<PropertySource<?>> loadNacosData(String dataId, String group,
String fileExtension) {
String data = null;
try {
// 通过ConfigService加载配置内容问题
data = configService.getConfig(dataId, group, timeout);
if (StringUtils.isEmpty(data)) {
log.warn(
"Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
dataId, group);
return Collections.emptyList();
}
if (log.isDebugEnabled()) {
log.debug(String.format(
"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
group, data));
}
// 对文本内容进行解析
return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,fileExtension);
}
catch (NacosException e) {
log.error("get data from Nacos error,dataId:{} ", dataId, e);
}
catch (Exception e) {
log.error("parse data from Nacos error,dataId:{},data:{}", dataId, data, e);
}
return Collections.emptyList();
}
ConfigService
可以看出,最终加载配置内容文本通过ConfigService类进行加载,对应源码如下:
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
// 该方法是优先加载本地缓存的配置内容,如果本地缓存的配置内容已经存在,则优先使用本地缓存的配置信息。
// 默认配置: JM.SNAPSHOT.PATH -> user.home/nacos. 也就是说,优先查找JM.SNAPSHOT.PATH, 如果不存在,就使用user.home + /nacos作为目录
// 其中也包括了几个子目录:
// 1. /data: 存放配置地方
// 2. /data/config-data: 如果没有租户信息,则使用该目录
// 3. /data/config-data-tenant/{tenant}: 如果包含了租户信息,则以及租户路径进行查找
String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
// 如果本地配置不存在,则从远程去获取配置
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
cr.setContent(response.getContent());
cr.setEncryptedDataKey(response.getEncryptedDataKey());
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
...
}
content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
}
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
这里不去探讨获取配置的方式是什么,所以到这一步,我们就从nacos-server中获取到了配置文件。
当我们拿到nacos配置文件之后,将配置放到environment的sources列表头部,替身获取配置优先级,以此达到配置使用目的。


