Spring Cloud 專案 SpringApplication Run 執行兩次解密
阿新 • • 發佈:2020-06-24
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
等埠衝突。