自定義初始化器

自定義初始化器

在 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();
    }
}


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

源碼地址:點我

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章