自定義初始化器
在 Springboot 中使用自定義初始化器大致可以分爲以下兩個步驟:
- 自定義初始化器,一般是實現 ApplicationContextInitializer 接口。
- 註冊初始化器。
爲何要自定義初始化器
Spring 是一個擴展性很強的容器框架,爲開發者提供了豐富的擴展入口,其中一個擴展點便是 ApplicationContextInitializer (應用上下文初始化器 )。
ApplicationContextInitializer 是 Spring 在執行 ConfigurableApplicationContext.refresh() 方法對應用上下文進行刷新之前調用的一個回調接口,用來完成對 Spring 應用上下文個性化的初始化工作,該接口定義在 org.springframework.context 包中,其內部僅包含一個 initialize() 方法,其定義代碼如下。
package org.springframework.context;
/**
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
關聯源碼
Springboot定義的 ApplicationContextInitializer 接口的實現類有下面幾個,如圖所示。
-
DelegatingApplicationContextInitializer
DelegatingApplicationContextInitializer 初始化器負責讀取 核心配置文件 context.initializer.classes 配置項指定的初始化器, 並調用它們的 initialize() 方法來完成對應用上下文的初始化工作。
-
ContextIdApplicationContextInitializer
ContextIdApplicationContextInitializer 初始化器的作用是給應用上下文設置一個ID, 這個ID由 name 和 index 兩個部分組成。 其中 name 會依次從讀取核心配置的以下三個屬性,如果存在則立即使用,若不存在則繼續查找下一個, 都不存在則取默認值 “application”。 spring.application.name vcap.application.name spring.config.name index 會依次從核心配置的以下幾個屬性獲取,如果存在則立即使用,若不存在則繼續查找下一個,都不存在則爲空。 vcap.application.instance_index spring.application.index server.port PORT 所以,ID的格式應爲 [application.name][:application.index] ,例如,application:8080 。
-
ConfigurationWarningsApplicationContextInitializer
ConfigurationWarningsApplicationContextInitializer 初始化器用來
對常見的由於配置錯誤而引起的警告進行打印報告
-
ServerPortInfoApplicationContextInitializer
ServerPortInfoApplicationContextInitializer 初始化器通過監聽
EmbeddedServletContainerInitializedEvent 事件,來對內部服務器實際要監聽的端口號進行屬性設置。
-
SharedMetadataReaderFactoryContextInitializer
SharedMetadataReaderFactoryContextInitializer 初始化器用來創建一個可以
在 ConfigurationClassPostProcessor 和Spring Boot 之間共享的CachingMetadataReaderFactory。
-
AutoConfigurationReportLoggingInitializer
AutoConfigurationReportLoggingInitializer 初始化器用來將 ConditionEvaluationReport
記錄的條件評估詳情輸出到日誌,默認使用 DEBUG 級別,當有異常問題發生時會使用 INFO 級別。
各個初始化器的執行順序如下: DelegatingApplicationContextInitializer --> ContextIdApplicationContextInitializer --> ConfigurationWarningsApplicationContextInitializer -->ServerPortInfoApplicationContextInitializer -->SharedMetadataReaderFactoryContextInitializer --> AutoConfigurationReportLoggingInitializer
實戰
創建model(Springbootinitializer)
第一種創建方式Initializer的方式
package com.zhang.initailizer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
public class MyFirstApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
Map<String,Object> mps = new HashMap<>();
mps.put("key1","zhangsan");
MapPropertySource mapPropertySource = new MapPropertySource("first",mps);
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("run firstInitializer");
}
2.創建META-INF和spring.factories
org.springframework.context.ApplicationContextInitializer=com.zhang.initailizer.MyFirstApplicationContextInitializer
3.創建一個service文件夾
當一個類實現了這個接口(ApplicationContextAware)之後,這個類就可以方便獲得ApplicationContext中的所有bean。換句話說,就是這個類可以直接獲取spring配置文件中,所有有引用到的bean對象。
package com.zhang.service;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Testservice implements ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String test(){
return applicationContext.getEnvironment().getProperty("key1");
}
}
4.創建一個controller文件夾
package com.zhang.controller;
import com.zhang.service.Testservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private Testservice testservice;
@RequestMapping("/test")
public String test(){
return testservice.test();
}
}
第一種啓動方式
通過在CLASSPATH/META-INF/spring.factories中添加 org.springframework.context.ApplicationContextInitializer 配置項進行註冊。
- 實現ApplicationContextInitializer接口
- spring.factories 添加接口實現
- key值爲org.springframework.context.ApplicationContextInitializer
第二種啓動方式
package com.zhang;
import com.zhang.initailizer.MySecondApplicationContextInitializer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
//SpringApplication.run(Application.class, args);
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addInitializers(new MySecondApplicationContextInitializer());
ConfigurableApplicationContext context = springApplication.run(args);
}
}
- 實現ApplicationContextInitializer接口
- springApplication類初始化設置進去
第三種啓動方式
在 Springboot 核心配置文件 application.properties 中增加 context.initializer.classes = [ 初始化器全類名 ] 進行註冊。
- 實現ApplicationContextInitializer接口
- 在application.properties填寫接口實現
- key值爲context.initializer.classes
三種方式實現ApplicationContextInitializer接口 ,order值越小越先執行,不過 application.properties定義優先於其他方法。
源碼地址:點我