1. 程式人生 > 其它 >Spring 中初始化器的實現和自定義

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

ServerPortInfoApplicationContextInitializer 初始化器通過監聽 EmbeddedServletContainerInitializedEvent 事件,來對內部伺服器實際要監聽的埠號進行屬性設定。 
  • SharedMetadataReaderFactoryContextInitializer

SharedMetadataReaderFactoryContextInitializer 初始化器用來建立一個可以在 ConfigurationClassPostProcessor 和Spring Boot 之間共享的CachingMetadataReaderFactory。 
  • AutoConfigurationReportLoggingInitializer

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定義優先於其他方法。