ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Apollo 整合 Spring Boot 原理分析

2021-06-26 20:05:08  阅读:383  来源: 互联网

标签:Apollo 配置 spring boot Boot environment Spring apollo 加载


一、使用

1、apollo是携程开发的一个开源的分布式配置中心,spring boot项目如果需要整合apollo,需要在pom.xml中添加如下依赖:

<dependency>
   <groupId>com.ctrip.framework.apollo</groupId>
   <artifactId>apollo-client</artifactId>
   <version>1.6.2</version>
</dependency>

2、需要在spring boot项目的application.properties(或yml)文件中添加如下配置:

// 该应用在apollo里对应的app id
app.id=xxx
// apollo服务端地址
apollo.meta=http://127.0.0.1:8080
// 启用apollo
apollo.bootstrap.enabled=true
// 开启饥饿加载
apollo.bootstrap.eagerLoad.enabled=true

3、启动类上加上@EnableApolloConfig注解

二、原理分析

1、找到apollo-clinet.jar下的spring.factories文件并打开,如下所示:
在这里插入图片描述
配置了ApolloAutoConfiguration和ApolloApplicationContextInitializer两个类。第一个类是自动配置类,第二个类实现了ApplicationContextInitializer和EnvironmentPostProcessor接口,是apollo能整合spring boot的一个关键因素。

2、spring boot在启动过程中(参考:spring boot 启动流程),首先会加载ApplicationContextInitializer和ApplicationListener(事件监听器,代表:ConfigFileApplicationListener),然后加载SpringApplicationRunListener(发布事件用,代表:EventPublishingRunListener),再后面会调用org.springframework.boot.SpringApplication#prepareEnvironment 方法,这时候会发布一个ApplicationEnvironmentPreparedEvent事件,正好ConfigFileApplicationListener监听了这个事件,代码如下:

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}

	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		// 从spring.factories中加载EnvironmentPostProcessor的实现类,从而找到ApolloApplicationContextInitializer
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			// 这里会调用到ApolloApplicationContextInitializer重写的postProcessEnvironment方法
			postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
		}
	}

	List<EnvironmentPostProcessor> loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
	}

	// ConfigFileApplicationListener本身也实现了EnvironmentPostProcessor,所以他自己也重写了postProcessEnvironment方法,这个方法就是将配置文件属性源添加到环境中。
	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		addPropertySources(environment, application.getResourceLoader());
	}

3、看看com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer#postProcessEnvironment 方法做了什么事

  @Override
  public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {

    // should always initialize system properties like app.id in the first place
    // 找app.id和apollo.meta这些属性
    initializeSystemProperty(configurableEnvironment);

	// 判断是否开启了饥饿加载,如果没开启就暂时放弃加载apollo中的配置
    Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);

    //EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
    if (!eagerLoadEnabled) {
      return;
    }

	// 如果开启了饥饿加载,再判断是否启用了apollo,如果启用了就开始加载
    Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);

    if (bootstrapEnabled) {
      initialize(configurableEnvironment);
    }

  }

    protected void initialize(ConfigurableEnvironment environment) {
		// 先判断是不是已经加载过了,刚开始肯定没有加载,如果加载过了就不重复加载了
	    if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
	      //already initialized
	      return;
	    }
	
		// apollo配置的默认namespace是application.properties,也可以通过apollo.bootstrap.namespaces自行指定
	    String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
	    logger.debug("Apollo bootstrap namespaces: {}", namespaces);
	    // 如果指定了多个,通过逗号分割
	    List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);
	
	    CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
	    for (String namespace : namespaceList) {
	      // 加载配置
	      Config config = ConfigService.getConfig(namespace);
	
	      composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
	    }
	
		// 这里将apollo的配置放到PropertySources的第一个,所以他的配置优先级是最高的
		// spring boot读取配置就是按照优先级从高到低的顺序加载,如果读到了就直接返回了,所以要注意配置覆盖问题
	    environment.getPropertySources().addFirst(composite);
    }

4、上面的流程是开启了apollo饥饿加载时的流程,如果没有开启,在prepareEnvironment 环节不会加载apollo的配置,而是在调用org.springframework.boot.SpringApplication#prepareContext 方法时加载,这个方法会调用 applyInitializers(context),也就是调用ApplicationContextInitializer#initialize(context), ApolloApplicationContextInitializer实现了ApplicationContextInitializer接口,并重写了initialize方法

  @Override
  public void initialize(ConfigurableApplicationContext context) {
    ConfigurableEnvironment environment = context.getEnvironment();

	// 同样先判断是否开启了apollo
    if (!environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false)) {
      logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
      return;
    }
    logger.debug("Apollo bootstrap config is enabled for context {}", context);

	// 这里就是调用第3步中提到过的initialize方法,前面没有加载过的话这里也会进行加载
    initialize(environment);
  }

如果是在这个时候加载,有些配置就不能配置在apollo中,比如日志相关的配置(如logging.level.root=info或logback-spring.xml中的参数),因为日志相关的加载在这一步之前。顺序是:加载 Bootstrap 属性和应用程序属性 -----> 加载 Apollo 配置属性 ----> 初始化日志系统

标签:Apollo,配置,spring,boot,Boot,environment,Spring,apollo,加载
来源: https://blog.csdn.net/oyc619491800/article/details/118252797

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有