为了账号安全,请及时绑定邮箱和手机立即绑定

springcloud情操陶冶-springcloud context(一)

标签:
Spring Cloud

基于前文对springcloud的浅谈,本文则借鉴springcloud config对微服务进行管中窥豹

前言

springcloud是基于springboot开发的,所以读者在阅读此文前最好已经了解了springboot的工作原理。本文将不阐述springboot的工作逻辑

ConfigServerApplication

spring-cloud-config-server模块的启动类,也是springcloud config作为服务端的入口类,优先看下其内部源码,简洁

@Configuration@EnableAutoConfiguration@EnableConfigServerpublic class ConfigServerApplication {    public static void main(String[] args) {        new SpringApplicationBuilder(ConfigServerApplication.class)
                .properties("spring.config.name=configserver").run(args);
    }

}

相比我们常见的springboot启动类,多了一个注解@EnableConfigServer和采取SpringApplication帮助类来运行springcloud环境。


@EnableConfigServer注解的引入其实是引入了org.springframework.cloud.config.server.config.server.config.ConfigServerConfiguration类,此类无特殊,就是注册了一个Bean对象

    class Marker {}    @Bean
    public Marker enableConfigServerMarker() {        return new Marker();
    }

至于有什么用,还不知晓,继续往下探索把


SpringApplicationBuilder的使用方式就不深入分析了,就是帮助配置和调用SpringApplication,用了适配器的设计思想。此处讲述下其设置属性spring.config.name=configserver,根据springboot的工作方式,其会读取类环境下的名为configserver.yaml/configserver.properties/configserver.yml加载至环境变量中

Cloud Context

springboot cloud context在官方的文档中在第一点被提及,是用户ApplicationContext的父级上下文,称为BootstrapContext。根据springboot的加载机制,很多第三方以及重要的Configuration配置均是保存在了spring.factories文件中。笔者翻阅了spring-cloud-context模块下的对应文件,见如下

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

涉及的比较多,笔者本文关注BootstrapApplicationListener监听器,其讲述了BootstrapContext是如何被加载以及加载的时机

BootstrapApplicationListener

按照顺序分析此监听器


1.优先看下其类结构

public class BootstrapApplicationListener
        implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
        }

此监视器是用于响应ApplicationEnvironmentPreparedEvent应用环境变量预初始化事件,表明BootstrapContext的加载时机在用户上下文之前


2.接下来分析下其复写的方法onApplicationEvent(ApplicationEnvironmentPreparedEvent event)

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {        // 获取环境变量对象
        ConfigurableEnvironment environment = event.getEnvironment();        // 读取spring.cloud.bootstrap.enabled环境属性,默认为true。可通过系统变量设置
        if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,                true)) {            return;
        }        // don't listen to events in a bootstrap context
        if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {            return;
        }        // 寻找当前环境是否已存在BootstrapContext
        ConfigurableApplicationContext context = null;
        String configName = environment
                .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");        for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
                .getInitializers()) {            if (initializer instanceof ParentContextApplicationContextInitializer) {
                context = findBootstrapContext(
                        (ParentContextApplicationContextInitializer) initializer,
                        configName);
            }
        }        // 如果还没有被创建,则开始创建
        if (context == null) {
            context = bootstrapServiceContext(environment, event.getSpringApplication(),
                    configName);            // 注册注销监听器
            event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
        }        // 加载BoostrapContext上的ApplicationContextInitializers到用户Context上
        apply(context, event.getSpringApplication(), environment);
    }

逻辑很简单,笔者梳理下

  • spring.cloud.bootstrap.enabled 用于配置是否启用BootstrapContext,默认为true。可采取系统变量设定

  • spring.cloud.bootstrap.name 用于加载bootstrap对应配置文件的别名,默认为bootstrap

  • BootstrapContext上的beanType为ApplicationContextInitializer类型的bean对象集合会被注册至用户的Context上


3.重点看下BootstrapContext的创建过程,源码比较长,但笔者认为还是很有必要拿出来

    /**
     *
     *  create bootstrap context
     *
     * @param environment   全局Environment
     * @param application   用户对应的Application
     * @param configName    bootstrapContext对应配置文件的加载名,默认为bootstrap
     * @return  bootstrapContext
     */
    private ConfigurableApplicationContext bootstrapServiceContext(
            ConfigurableEnvironment environment, final SpringApplication application,
            String configName) {        // create empty environment
        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
        MutablePropertySources bootstrapProperties = bootstrapEnvironment
                .getPropertySources();        for (PropertySource<?> source : bootstrapProperties) {
            bootstrapProperties.remove(source.getName());
        }        // 读取spring.cloud.bootstrap.location属性,一般通过系统变量设置,默认为空
        String configLocation = environment
                .resolvePlaceholders("${spring.cloud.bootstrap.location:}");
        Map<String, Object> bootstrapMap = new HashMap<>();
        bootstrapMap.put("spring.config.name", configName);
        bootstrapMap.put("spring.main.web-application-type", "none");        // 加载bootstrapContext配置文件的路径,与spring.config.name搭配使用
        if (StringUtils.hasText(configLocation)) {
            bootstrapMap.put("spring.config.location", configLocation);
        }
        bootstrapProperties.addFirst(                new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));        for (PropertySource<?> source : environment.getPropertySources()) {            if (source instanceof StubPropertySource) {                continue;
            }
            bootstrapProperties.addLast(source);
        }        // use SpringApplicationBuilder to create bootstrapContext
        SpringApplicationBuilder builder = new SpringApplicationBuilder()                // 此处activeProfiles是通过系统变量设置的,此处稍微备注下
                .profiles(environment.getActiveProfiles())
                .bannerMode(Mode.OFF)                // 应用bootstrap本身的环境变量
                .environment(bootstrapEnvironment)                // Don't use the default properties in this builder
                .registerShutdownHook(false).logStartupInfo(false)
                .web(WebApplicationType.NONE);        final SpringApplication builderApplication = builder.application();        // 配置入口函数类
        if (builderApplication.getMainApplicationClass() == null) {
            builder.main(application.getMainApplicationClass());
        }        
        if (environment.getPropertySources().contains("refreshArgs")) {
            builderApplication
                    .setListeners(filterListeners(builderApplication.getListeners()));
        }        // 增加入口类BootstrapImportSelectorConfiguration
        builder.sources(BootstrapImportSelectorConfiguration.class);        // create
        final ConfigurableApplicationContext context = builder.run();        // 设置bootstrapContext的别名为bootstrap
        context.setId("bootstrap");        // 配置bootstrapContext为用户Context的父类
        addAncestorInitializer(application, context);        // 合并defaultProperties对应的变量至childEnvironment
        bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
        mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);        return context;
    }

此处也对上述的代码作下简单的小结

  • spring.cloud.bootstrap.location变量用于配置bootstrapContext配置文件的加载路径,可用System设置,默认则采取默认的文件搜寻路径;与spring.cloud.bootstrap.name搭配使用

  • bootstrapContext对应的activeProfiles可采用spring.active.profiles系统变量设置,注意是System变量

  • bootstrapContext的重要入口类为BootstrapImportSelectorConfiguration,此也是下文的分析重点

  • bootstrapContext的contextId为bootstrap,且其会被设置为用户Context的父类

  • 用户Context的别名为defaultProperties对应的变量会被bootstrapContext对应合并覆盖

通过上述的代码均可以得知,bootstrapContext也是通过springboot常见的SpringApplication方式来创建的,但其肯定有特别的地方,特别之处就在BootstrapImportSelectorConfiguration类。

后记

由于继续分析会导致篇幅过长,遂片段式,这样有助于深入理解以及后期回顾。下文便会主要分析下bootstrapContext额外的特点。

作者:南柯问天  出处:https://www.cnblogs.com/question-sky/p/10245384.html  本文版权归本人和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消