Spring 中初始化器的實現和自定義
技術標籤:Spring Boot 原始碼
自定義初始化器
在 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 初始化器通過監聽 EmbeddedServletContainerInitializedEvent 事件,來對內部伺服器實際要監聽的埠號進行屬性設定。
SharedMetadataReaderFactoryContextInitializer 初始化器用來建立一個可以在 ConfigurationClassPostProcessor 和Spring Boot 之間共享的CachingMetadataReaderFactory。
AutoConfigurationReportLoggingInitializer 初始化器用來將 ConditionEvaluationReport 記錄的條件評估詳情輸出到日誌,預設使用 DEBUG 級別,當有異常問題發生時會使用 INFO 級別。
各個初始化器的執行順序如下:
DelegatingApplicationContextInitializer --> ContextIdApplicationContextInitializer --> ConfigurationWarningsApplicationContextInitializer -->ServerPortInfoApplicationContextInitializer–>SharedMetadataReaderFactoryContextInitializer–> AutoConfigurationReportLoggingInitializer
實戰
第一種建立方式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;
/**
* Created by zhang on 2019-12-23.
*/
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;
/**
* Created by zhang on 2019-12-23.
*/
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;
/**
* Created by zhang on 2019-12-23.
*/
@RestController
public class TestController {
@Autowired
private Testservice testservice;
@RequestMapping("/test")
public String test(){
return testservice.test();
}
}
http://localhost:8888/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定義優先於其他方法。