美文网首页
Spring Cloud 项目 SpringApplicatio

Spring Cloud 项目 SpringApplicatio

作者: 冷冷zz | 来源:发表于2019-11-27 07:44 被阅读0次

    ApplicationContextInitializer介绍

    作用是在ConfigurableApplicationContext类型的ApplicationContext.refresh 操作
    之前,允许我们对ConfiurableApplicationContext增强处理的扩展。

    业务场景

    在实际开发过程中,web应用中需要编程方式对应用上下文做初始化。
    比如,注册属性源(bootstrap/application properties sources); 编码动态激活不同profile对应environment

    最近又个项目,要根据不同的环境 Linux/Windows 来加载不同的SDK 参数.

    自定义Condition 实现

    由于配置项极其的多,通过修改 ConfigurationProperties Bean 上自定义 @Conditional

    public class LinuxCondition  implements Condition{
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            String property = context.getEnvironment().getProperty("os.name");
                ...
            return  property.contains("linux");
        }
    }
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional({LinuxCondition.class})
    public @interface ConditionOnLinux {}
    

    由于SDK 设计的配置类抽取的太多,拆分的不太合理,在不破坏文物的情况下所以放弃了这种方式

    自定义 ApplicationContextInitializer 实现

    根据上下文环境,加载 resource 目录,不同环境的配置文件

    public class SelectApplicationContextInitializer implements ApplicationContextInitializer {
        @Override
        public void initialize(ConfigurableApplicationContext context) {
            ConfigurableEnvironment env = context.getEnvironment();
            MutablePropertySources mps = env.getPropertySources();
    
            String property = env.getProperty("os.name");
    
            if (property.contains("Mac OS X")) {
                mps.addLast(new ResourcePropertySource(new ClassPathResource("linux.properties")));
            } else {
                mps.addLast(new ResourcePropertySource(new ClassPathResource("window.properties")));
            }
    
        }
    }
    

    问题: initialize 执行两次

    相关功能抽取成starter,运行在单体的 Spring Boot 项目,若加入 Spring Cloud Context 则会执行两次 上文代码

    SpringApplication.run

    • 为了找出问题真凶,在 SpringApplication run 方法下打上了断点。


    BootstrapApplicationListener

    一路跟到了 BootstrapApplicationListener.bootstrapServiceContext 方法。

    我们看看 bootstrapServiceContext 方法,

    SpringApplicationBuilder builder = (new SpringApplicationBuilder(new Class[0])).profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF).environment(bootstrapEnvironment).registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
    SpringApplication builderApplication = builder.application();
    if (builderApplication.getMainApplicationClass() == null) {
        builder.main(application.getMainApplicationClass());
    }
    
    if (environment.getPropertySources().contains("refreshArgs")) {
        builderApplication.setListeners(this.filterListeners(builderApplication.getListeners()));
    }
    
    builder.sources(new Class[]{BootstrapImportSelectorConfiguration.class});
    ConfigurableApplicationContext context = builder.run(new String[0]);
    context.setId("bootstrap");
    

    真相预警

    BootstrapApplicationListener 里,利用 SpringApplicationBuilder 进行了一次重启, 虽然是Run 两次但是第一次 并未到 启动容器等,所以出现
    Bean 加载两次,或者 运行容器 Tomcat 等端口冲突。

    image

    相关文章

      网友评论

          本文标题:Spring Cloud 项目 SpringApplicatio

          本文链接:https://www.haomeiwen.com/subject/dqmuwctx.html