Springboot--扩展外部化配置(三)

接着之前Springboot--扩展外部化配置(二) 来继续实现外部化的扩展。我们将剩余的几个扩展给介绍完毕。

  • 基于 ApplicationContextInitializer 扩展外部化配置属性源
  • 基于 SpringApplicationRunListener#contextPrepared 扩展外部化配置属性源
  • 基于 SpringApplicationRunListener#contextLoaded 扩展外部化配置属性源

前置内容

在Springboot--扩展外部化配置(一) 中我们有了解到在SpringBoot启动过程中在创建完 Environment之后,就会准备上下文org.springframework.boot.SpringApplication#prepareContext 。我们接下来的三种外部化配置实现方式 都是基于这个方法的执行顺序实现的。可以详细看一下贴出的源码中的注释。

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
    //执行ApplicationContextInitializer SPI接口 initialize
    //用于 基于 ApplicationContextInitializer 扩展外部化配置属性源
   applyInitializers(context);
    
   //SpringApplicationRunListener 实现类遍历监听contextPrepared
   //用于 基于 SpringApplicationRunListener#contextPrepared 扩展外部化配置属性源
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }

   // Add boot specific singleton beans
   context.getBeanFactory().registerSingleton("springApplicationArguments",
         applicationArguments);
   if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
   }

   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
   //SpringApplicationRunListener 实现类遍历监听contextLoaded
    //用于 基于 SpringApplicationRunListener#contextLoaded 扩展外部化配置属性源
   listeners.contextLoaded(context);
}

通过我标注注释的地方,大家可能也就了解到了之后我们准备怎么去实现剩下的外部化配置方式。其实无非就是通过SpringBoot在初始化上下文之前,调用可以调用的对外接口,进行配置。

这里我们主要是注意因为我们在扩展外部化配置时 使用的是 org.springframework.core.env.MutablePropertySources#addFirst 方法,默认会把配置的外部化资源放在第一位。所以我们要注意prepareContext() 方法中的这些过程的执行顺序,换局话说,执行越晚,那么其配置的资源则会放在最前面。

基于 ApplicationContextInitializer 扩展外部化配置属性源

实现基础

现在我们就使用在 org.springframework.boot.SpringApplication#prepareContext中的 applyInitializers(context); 方法(目的是对应用程序上下文进行初始化),做切入点,让我们也简单实现一个自定义外部资源的初始化操作。

具体实现

1.实现ApplicationContextInitializer `接口类 ExternalizeApplicationContextInitializer.java

/**
 * @ClassName ExternalizeApplicationContextInitializer
 * @Description 自定义上下文初始化实现类
 * @Author Neal
 * @Date 2019/1/23 19:05
 * @Version 1.0
 */
public class ExternalizeApplicationContextInitializer implements ApplicationContextInitializer {


    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //获取environment 对象
        ConfigurableEnvironment environment = applicationContext.getEnvironment();

        //获取项目跟路径
        String classpath = ExternalizeApplicationContextInitializer.class.getResource("/").getPath();
        //获取PropertySource组合对象
        MutablePropertySources propertySources = environment.getPropertySources();
        //获取自定义的外部化配置资源
        File file = new File(classpath +"config/applicationinitializer.properties");

        /**
         * 获取Property对象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //声明一个properties对象
            Properties properties = new Properties();
            // 加载字符流成为 Properties 对象
            properties.load(reader);
            //声明Spring内置PropertiesPropertySource对象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---ExternalizeApplicationContextInitializer",properties);
            //将配置资源放到其他配置资源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

2.配置自定义的外部化资源,resources\config\applicationinitializer.properties

#ExternalizeApplicationContextInitializer 对应的配置
test=5

3.将自定义的ApplicationContextInitializer 加入到 resources\META-INF\spring.factories

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
neal.externalized.listener.ExternalizePropertyListener
#ApplicationListener
org.springframework.context.ApplicationListener=\
neal.externalized.listener.ExternalizePropertyEventListener

#EnvironmentPostProcessor
org.springframework.boot.env.EnvironmentPostProcessor=\
neal.externalized.listener.ExternalizePropertyPostProcessor

#ApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer=\
neal.externalized.listener.ExternalizeApplicationContextInitializer

执行结果

我们启动容器,查看控制台输出

p8.png

这里我们注意到,我们的测试的值已经变成了所配置的5,但是还有一点 ConfigurationPropertySourcesPropertySource 这个类的执行是在 我们自定义ExternalizeApplicationContextInitializer之前的,所以我们刚刚自定义外部化配置才会放在首位执行。

基于 SpringApplicationRunListener#contextPrepared 扩展外部化配置属性源

实现基础

使用在 org.springframework.boot.SpringApplication#prepareContext中的 listeners.contextPrepared(context); 方法(上下文准备完成),做切入点,简单实现一个自定义外部资源的初始化操作。

这段的实现其实在我们之前已经自定义了SpringApplicationRunListener 事件监听,就是我们介绍的第一种扩展方式(基于 SpringApplicationRunListener#environmentPrepared的实现方式)中使用的ExternalizePropertyListener

具体实现

1.重写ExternalizePropertyListener#contextPrepared() 方法。这里用到了读取Properties文件的方法,由于是DEMO,我就不单独抽出来重构了。

/**
 * 扩展 {@link PropertySource}
 */
public class ExternalizePropertyListener implements SpringApplicationRunListener,Ordered {

    private final SpringApplication application;

    private final String[] args;

    public ExternalizePropertyListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

    @Override
    public void starting() {

    }

    /**
     * 基于 `SpringApplicationRunListener#environmentPrepared`的实现方式
     * @param environment
     */
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

        //获取项目跟路径
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //获取PropertySource组合对象
        MutablePropertySources propertySources = environment.getPropertySources();

        //获取自定义的外部化配置资源
        File file = new File(classpath +"config/externalizepropertylistener.properties");
        /**
         * 获取Property对象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //声明一个properties对象
            Properties properties = new Properties();
            // 加载字符流成为 Properties 对象
            properties.load(reader);
            //声明Spring内置PropertiesPropertySource对象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---ExternalizePropertyListener",properties);
            //将配置资源放到其他配置资源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     *     基于 `SpringApplicationRunListener#contextPrepared` 扩展外部化配置属性源
     */
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        //获取environment 对象
        ConfigurableEnvironment environment = context.getEnvironment();

        //获取项目跟路径
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //获取PropertySource组合对象
        MutablePropertySources propertySources = environment.getPropertySources();
        //获取自定义的外部化配置资源
        File file = new File(classpath +"config/contextprepared.properties");

        /**
         * 获取Property对象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //声明一个properties对象
            Properties properties = new Properties();
            // 加载字符流成为 Properties 对象
            properties.load(reader);
            //声明Spring内置PropertiesPropertySource对象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---contextPrepared",properties);
            //将配置资源放到其他配置资源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

    }

    @Override
    public void started(ConfigurableApplicationContext context) {

    }

    @Override
    public void running(ConfigurableApplicationContext context) {

    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {

    }
    //加载顺序在EventPublishingRunListener之前
    // 这么做是为了之后的外部化配置展示
    @Override
    public int getOrder() {
        return -1;
    }
}

这里我贴出了全部方法,在这一部分,我们只需要关心#contextPrepared()的实现。

2.配置自定义的外部化资源,resources\config\contextprepared.properties

#SpringApplicationRunListener#contextPrepared 对应的配置
test=6

3.由于之前在resources\META-INF\spring.factories添加过监听,所以这里不需要做任何操作

执行结果

p9.png

执行结果,不出意外,确实是我们最新配置的值。

基于 SpringApplicationRunListener#contextLoaded 扩展外部化配置属性源

实现基础

我们接着使用在 org.springframework.boot.SpringApplication#prepareContext中的 listeners.contextLoaded(context); 方法(加载应用上下文),做切入点,简单实现一个自定义外部资源的初始化操作。

同样,这段的实现跟上面的实现一样,都是在之前介绍的第一种扩展方式(基于 SpringApplicationRunListener#environmentPrepared的实现方式)中使用的ExternalizePropertyListener

具体实现

1.重写ExternalizePropertyListener#contextLoaded()方法。这里一样我们直接copy之前的实现,不做重构。

/**
 * 扩展 {@link PropertySource}
 */
public class ExternalizePropertyListener implements SpringApplicationRunListener,Ordered {

    private final SpringApplication application;

    private final String[] args;

    public ExternalizePropertyListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

    @Override
    public void starting() {

    }

    /**
     * 基于 `SpringApplicationRunListener#environmentPrepared`的实现方式
     * @param environment
     */
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

        //获取项目跟路径
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //获取PropertySource组合对象
        MutablePropertySources propertySources = environment.getPropertySources();

        //获取自定义的外部化配置资源
        File file = new File(classpath +"config/externalizepropertylistener.properties");
        /**
         * 获取Property对象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //声明一个properties对象
            Properties properties = new Properties();
            // 加载字符流成为 Properties 对象
            properties.load(reader);
            //声明Spring内置PropertiesPropertySource对象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---ExternalizePropertyListener",properties);
            //将配置资源放到其他配置资源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     *     基于 `SpringApplicationRunListener#contextPrepared` 扩展外部化配置属性源
     */
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        //获取environment 对象
        ConfigurableEnvironment environment = context.getEnvironment();

        //获取项目跟路径
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //获取PropertySource组合对象
        MutablePropertySources propertySources = environment.getPropertySources();
        //获取自定义的外部化配置资源
        File file = new File(classpath +"config/contextprepared.properties");

        /**
         * 获取Property对象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //声明一个properties对象
            Properties properties = new Properties();
            // 加载字符流成为 Properties 对象
            properties.load(reader);
            //声明Spring内置PropertiesPropertySource对象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---contextPrepared",properties);
            //将配置资源放到其他配置资源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 基于 `SpringApplicationRunListener#contextLoaded` 扩展外部化配置属性源
     * @param context
     */
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        //获取environment 对象
        ConfigurableEnvironment environment = context.getEnvironment();

        //获取项目跟路径
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //获取PropertySource组合对象
        MutablePropertySources propertySources = environment.getPropertySources();
        //获取自定义的外部化配置资源
        File file = new File(classpath +"config/contextploaded.properties");

        /**
         * 获取Property对象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //声明一个properties对象
            Properties properties = new Properties();
            // 加载字符流成为 Properties 对象
            properties.load(reader);
            //声明Spring内置PropertiesPropertySource对象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---contextLoaded",properties);
            //将配置资源放到其他配置资源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void started(ConfigurableApplicationContext context) {

    }

    @Override
    public void running(ConfigurableApplicationContext context) {

    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {

    }
    //加载顺序在EventPublishingRunListener之前
    // 这么做是为了之后的外部化配置展示
    @Override
    public int getOrder() {
        return -1;
    }
}

这里也贴出了全部方法,在这一部分,我们只需要关心#contextLoaded()的实现。

2.配置自定义的外部化资源,resources\config\contextloaded.properties

#ExternalizePropertyListener.contextLoaded 对应的配置
test=7

3.同样,之前在resources\META-INF\spring.factories添加过监听,所以这里不需要做任何操作。

执行结果

p10.png

不出意外,我们也同样改变了扩展的值。

小结

对于外部化配置的方面,相信在之后的开发中会有很大的用途,特别是各种整合。希望在接下来的实际开发中,我们可以用到这些实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容