• SpringCloudContext模块


    简介

    SpringCloud这个框架本身是建立在SpringBoot基础之上的,所以使用SpringCloud的方式与SpringBoot相仿。也是通过类似如下代码进行启动。

    SpringApplication.run(XxxApplication.class, args);

    其中 XxxApplication.class 类上也需要添加 @SpringBootApplication注解。
    要使用SpringCloud框架,在pom文件中要确保引入 spring-cloud-starter 依赖包,spring-cloud-starter依赖如下的jar:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>
    </dependencies>
    

    其中 spring-cloud-context-x.y.z.RELEASE.jarspring-cloud-commons-x.y.z.RELEASE.jar 下的 META-INF 目录下都包含 spring.factories 文件,所以可以把这两个jar看作是springCloud程序的入口

    SpringCloud在构建上下文<即ApplicationContext实例>时,采用了Spring父子容器的设计,会在 SpringBoot构建的容器<后面称之为应用容器>之上创建一一父容器Bootstrap Application Context .

    那么SpringCloud设计出Bootstrap Application Context ,并把它作为应用容器的父容器的目的是什么呢?

    因为SpringCloud 作为一个分布式微服务框架,需要使用全局的配置中心,而配置中心的配置是可以提供给应用容器的,所以在应用容器初始化和实例化Bean之前需要先完成配置中心的实例化,这个任务就由Bootstrap Application Context 来完成,而配置中心的相关配置属性就从 bootstrap.propertiesbootstrap.yml 文件中读取。

    但要注意的是,在Bootstrap Application Context 启动工作完成之后,其从bootstrap.properties或bootstrap.yml文件中读取的配置,是会被应用容器对应的application.properties或yml文件中的同名属性覆盖的。

    从源码角度分析上面的论述

    1.代码运行时还是从SpringApplication实例的run方法开始 ,此处会触发 BootstrapApplicationListener 类中的代码 , Bootstrap Application Context 的创建就是通过这个监听器触发的

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        // 此处会加载spring-cloud-context提供的监听器org.springframework.cloud.bootstrap.BootstrapApplicationListener.class
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);            
            // 此处会发布ApplicationEnvironmentPreparedEvent事件,触发BootstrapApplicationListener中的代码
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments,
                           printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    

    2.Bootstrap Application Context 的实例化 ,由BootstrapApplicationListener类的 onApplicationEvent方法触发

    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();        
        // 可以通过环境变量 spring.cloud.bootstrap.enabled来禁止使用Bootstrap容器
        if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
            return;
        }
        // 由于Bootstrap容器在创建时还是会再次调用上面步骤1的代码,还会再次触发
        // BootstrapApplicationListener类这个方法,所以此处作个判断,
        // 如果当前是Bootstrap容器的处理,则直接返回
        if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
            return;
        }
        ConfigurableApplicationContext context = null;        
        // 获取配置文件的名字,默认为bootstrap.properties或.yml ,并且这个名字可以通过 spring.cloud.bootstrap.name在环境中配置
        String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
        for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
             .getInitializers()) {             
            // 从相应jar中的spring.factories文件中读取初始化器的配置类实例,如果这个实例类是
            // ParentContextApplicationContextInitializer类型,则直接从该类中获取到父容器
            // 默认情况下,没有提供这样的类,下面这段代码会跳过
            if (initializer instanceof ParentContextApplicationContextInitializer) {
                context = findBootstrapContext(
                    (ParentContextApplicationContextInitializer) initializer,
                    configName);
            }
        }
        if (context == null) {            // 此处分析见步骤3
            context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
            event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
        }
    
        apply(context, event.getSpringApplication(), environment);
    }
    

    3.bootstrap容器的创建

    private ConfigurableApplicationContext bootstrapServiceContext(
        ConfigurableEnvironment environment, final SpringApplication application,
        String configName) {
        /**此处代码主要是从各处获取属性配置,此处忽略**/
        // TODO: is it possible or sensible to share a ResourceLoader?
        SpringApplicationBuilder builder = new SpringApplicationBuilder()
            .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
            .environment(bootstrapEnvironment)
            // Don't use the default properties in this builder
            .registerShutdownHook(false).logStartupInfo(false)
            .web(WebApplicationType.NONE);        
        // SpringApplicationBuilder的作用:1.构建SpringApplication  2.构建ApplicationContext        
        // 这里需要思考一下,SpringBoot在启动时已经构建了一个SpringApplication实例,为何此处又构建了一个         
        // 这是因为这个SpringApplication实例的构建环境和SringBoot原生构建的那个不同,看一下上一行代码就能明白
        final SpringApplication builderApplication = builder.application();
        if(builderApplication.getMainApplicationClass() == null){
            builder.main(application.getMainApplicationClass());
        }
        if (environment.getPropertySources().contains("refreshArgs")) {
            builderApplication.setListeners(filterListeners(builderApplication.getListeners()));
        }        
        // 从springFactories文件中查找BootstrapConfiguration的配置类
        builder.sources(BootstrapImportSelectorConfiguration.class);        
        // 构建出BootstrapContext
        final ConfigurableApplicationContext context = builder.run();        
        context.setId("bootstrap");
        // 设置BootstrapContext成为应用Context的父容器,此处分析见步骤4
        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);
        return context;
    }
    

    4.设置BootstrapContext成为应用Context的父容器 (在SpringApplication实例中动态添加了一个初始化器,相当于给应用Context埋了个雷)

    private void addAncestorInitializer(SpringApplication application, ConfigurableApplicationContext context) {
        boolean installed = false;        
        // 从spring.factories文件中获取初始化器的配置类且类型为AncestorInitializer
        for (ApplicationContextInitializer<?> initializer : application.getInitializers()) {
            if (initializer instanceof AncestorInitializer) {
                installed = true;
                // New parent
                ((AncestorInitializer) initializer).setParent(context);
            }
        }        
        // 默认情况下是没有配围置AncestorInitializer这样的类,此处是则执行由BootstrapListener提供的内部类
        if (!installed) {            
            // 将BootstrapContext作为父容器传到AncestorInitializer实例中,并将其放入SpringApplication实例的初始器列表中
            application.addInitializers(new AncestorInitializer(context));
        }
    }
    

    5.初始化器AncestorInitializer被触发,是由应用Context的处理触发的

    public void initialize(ConfigurableApplicationContext context) {  
        // 这个context是应用Context
        while (context.getParent() != null && context.getParent() != context) {
            context = (ConfigurableApplicationContext) context.getParent();
        }
        reorderSources(context.getEnvironment());            
        // 完成应用容器的父容器的设置
        new ParentContextApplicationContextInitializer(this.parent)
            .initialize(context);
    }
    

    6.ParentContextApplicationContextInitializer代码

    private static class ParentContextApplicationContextInitializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        private final ApplicationContext parent;
    
        ParentContextApplicationContextInitializer(ApplicationContext parent) {
            this.parent = parent;
        }
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            // 设置应用Context的父容器
            applicationContext.setParent(this.parent);
        }
    
    }
    

    总结

    1. SpringCloud Context模块的功能:主要是构建了一个Bootstrap容器,并让其成为原有的SpringBoot程序构建的容器的父容器。
    2. Bootstrap容器的作用:是为了预先完成一些Bean的实例化工作,可以把Bootstrap容器看作是先头部队。
    3. Bootstrap容器的构建:是利用了SpringBoot的事件机制,当SpringBoot的初始化Environment准备好之后会发布一个事件,这个事件的监听器将负责完成Bootstrap容器的创建。构建时是使用SpringApplicationBuilder类来完成的。
    4. 如何让Bootstrap容器与应用Context 建立父子关系:由于Bootstrap容器与应用Context都是关联着同一个SpringApplication实例,Bootstrap容器自己完成初始化器的调用之后,会动态添加了一个初始化器 AncestorInitializer,相当于给应用Context埋了个雷,这个初始化器在应用容器进行初始化器调用执行时,完成父子关系的设置。

    扩展

    1. spring-cloud-context源码解读

    原文链接 https://www.cnblogs.com/hzhuxin/p/10496762.html

  • 相关阅读:
    TCP可靠传输的实现
    jquery实现调用webservice
    linux下查看磁盘空间 [转]
    Linux系统中如何添加自己的库文件路径
    linux登录windows服务器
    QT的动态翻译功能,可能依赖于消息(事件)机制
    Markdown 语法简体中文版
    SpringMVC,Spring,Hibernate,Mybatis架构开发搭建之SpringMVC部分
    C#委托的介绍(delegate、Action、Func、predicate)
    SOA体系结构基础培训教程
  • 原文地址:https://www.cnblogs.com/gotodsp/p/15604935.html
Copyright © 2020-2023  润新知