前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >EnvironmentPostProcessor自定义配置

EnvironmentPostProcessor自定义配置

作者头像
写一点笔记
发布2022-08-11 17:01:37
4480
发布2022-08-11 17:01:37
举报
文章被收录于专栏:程序员备忘录程序员备忘录

我们想着这块把spring相关的有用的接口都过一下,相当于二次消化吧。其实仔细反思我们这一路的学习,我们发现java这块还是有很多要学的,就日常开发的spring来说,就咋写的那些笔记呀,只是毛毛雨。而spring也仅仅是开发的一部分,你想想还有各种中间件技术,是不是感觉头都要大了,没有一定的深度的学习,恐怕很难有质的改变吧。

这块我们学习一下接口EnvironmentPostProcessor,这块我们可以理解为环境变量的处理器。那么这个接口我们在项目中应该如何使用呐?首先我们需要明白的是一个接口的使用必然要涉及其入参和返回值。一般来说java都是引用传值,因此spring的接口都是void的。这样呐,我们就更注重对入参的修改。在解决了功能性质的思考之后呐,我们想着如何让spring识别我们这样一个自定义类。一般来说对于一般的bean,可以通过注解来搞定,为啥是注解呐?因为spring在实例化之前对进行类级别注解的扫描,在最后才进行实例化的。显然如果我们的这里的EnvironmentPostProcessor接口肯定是在其他bean实例化之前实例化的,故此呐,也就只有一种姿势了,那就是spring.factories文件了。基于上述的理解呐,我们写一个demo吧!

代码语言:javascript
复制
//自定义一个配置处理器
public class MyProcessEnvironment implements EnvironmentPostProcessor {


    /**
     * 因为java是引用传值,因此我们这里可以对配置类进行各种定制化操作,除此之外,我们可以懂启动类application的参数进行修改
     * @param environment 配置类
     * @param application 启动类
     */
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        System.out.println("配置处理...");
        System.out.println("现有的配置"+environment.toString());


        //添加一个配置
        Properties properties=new Properties();
        properties.put("tian","tianjingle");
        properties.put("server.port","9999");
        PropertiesPropertySource propertiesPropertySource=new PropertiesPropertySource("a",properties);
        environment.getPropertySources().addLast(propertiesPropertySource);
        //设置spring使用a的配置
        environment.setActiveProfiles("a");
        System.out.println(application.toString());
        
        
        //修改application的对象
        //直接修改
        application.setBannerMode(Banner.Mode.OFF);
    }
}

按照上边的理解,我们在项目中创建spring.factories文件,并指定我们的EnvironmentPostProcessor

代码语言:javascript
复制
org.springframework.boot.env.EnvironmentPostProcessor=\
  com.example.demo.MyProcessEnvironment

通过上述改造,我们就可以自己定义配置文件了,不仅如此,我们还可以在spring启动之前对springapplication的属性进行直接改变。

知道怎么干很简单,我们要知道为什么,这也是咋写文章的原因。挖掘深刻的背后是我们应该干的事。所以我们debug源码看看。这块我们大概的讲解一下源码,因为全量的话太多了。

如图所示的黄色标记为我们重点要看的代码,作者通过debug发现,spring的配置处理器方法的执行是由springapplication监听器触发的,之前分析说springapplicationlistener伴随着springapplication的生命周期。

代码语言:javascript
复制
在获取springapplication监听器的时候
    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class[] types = new Class[]{SpringApplication.class, String[].class};
//注意这里的getSpringFactoriesInstances方法,该方法就是从spring.factories文件中读取SpringApplicationRunListener.class对应的类,也就是等号之后的类。获取之后肯定是需要实例化的,但是实例化的过程需要参数,注意这里传入了this,也就是监听器中包含了springApplication这个类
        return new SpringApplicationRunListeners(logger,
this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
    }

通过上述获取springApplicationRunListenner的方法之后,spring运行监听器就加载好了。加载好listenners之后,springApplication逐个走starting方法。之后就开始执行我们标记的2方法。

//这里逐个监听器的执行environmentPrepared方法。

代码语言:javascript
复制
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        this.doWithListeners("spring.boot.application.environment-prepared", (listener) -> {
            listener.environmentPrepared(bootstrapContext, environment);
        });
    }
代码语言:javascript
复制
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
//广播ApplicationEnvironmentPreparedEvent
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
    }

注意这里的事件为ApplicationEnvironmentPreparedEvent,我们继续跟踪。

代码语言:javascript
复制
//处理配置的事件   
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
//获取配置处理器,也就是我们自定义的配置处理器
        Iterator var4 = this.getEnvironmentPostProcessors(event.getBootstrapContext()).iterator();


        while(var4.hasNext()) {
            EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var4.next();
            postProcessor.postProcessEnvironment(environment, application);
        }


    }

这块我们的问题是我们的processenviromment何时初始化的,作者不断的debug,终于找到了两。

我们万万没想到的是这块使用的是EnvironmentPostProcessorApplicationListener,它本质上是一个监听器,所以在处理广播事件的时候,它是会接受消息的。

我们看一下这个监听器的初始化方法。

代码语言:javascript
复制
 public EnvironmentPostProcessorApplicationListener() {
        this(EnvironmentPostProcessorsFactory.fromSpringFactories(EnvironmentPostProcessorApplicationListener.class.getClassLoader()));
    }
//实例化一个配置处理器的工厂,这个工厂的处理器均来自spring.factories文件。
    static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
        return new ReflectionEnvironmentPostProcessorsFactory(SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
    }

所以在接受到消息的时候,配置的工厂将所有的配置后置处理器返回来。然后逐个去执行。

而在消费消息的时候,也做了判断,如下所示:

代码语言:javascript
复制
 public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            this.onApplicationPreparedEvent();
        }
        if (event instanceof ApplicationFailedEvent) {
            this.onApplicationFailedEvent();
        }
    }

小节:springapplicaton启动的时候将各种listener实例化并保存,配置监听器的实例化的同时通过读取spring.factories文件加载我们的配置处理器。在springboot对配置信息进行处理调用时候,通过springboot监听器进行广播,我们的EnvironmentPostProcessorApplicationListener监听到消息之后,进行判断,然后通过配置后置处理器工厂获取所有的配置后置处理器,然后逐个去调用,然后对配置和springapplication启动类的属性进行修改。

今天学的有点猛了~都凌晨了~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 写点笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档