Spring源碼之ApplicationContextInitializer(二)

作用

ApplicationContextInitializer是Spring的一個擴展接口,在spring框架刷新上下文(refresh方法)之前調用ApplicationContextInitializer的實現類完成一些ApplicationContext子類的初始化工作,例如加入一個數據源或者激活profile的環境,ApplicationContextInitializer實現類通常會實現一個Ordered接口或者使用@Order接口進行排序。

配置方式

spring.factories

通過在META-INF目錄下的spring.factories配置org.springframework.context.ApplicationContextInitializer的實現類,將ApplicationContextInitializer的實現類加載到內存中,如下圖所示:
在這裏插入圖片描述

springApplication

通過調用springApplication的addInitializers方法將自定義的實現類加載到內存,如下圖所示
在這裏插入圖片描述

application.properties

通過在application.properties文件中配置context.initializer.classes屬性加載
在這裏插入圖片描述

自定義實現類

自定義一個ApplicationContextInitializer實現類,加入一個數據源,通過META-INF/spring.factories加載到內存,代碼如下所示:

@Order(1)
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("feidao", "zjl");
        MapPropertySource source = new MapPropertySource("firstInitializer", map);
        environment.getPropertySources().addLast(source);
        System.out.println("run firstInitializer");
    }
}
org.springframework.context.ApplicationContextInitializer=com.zjl.spring.initializer.FirstInitializer

實現一個SpringService,注入applicationContext,通過getProperties方法獲取feidao屬性。

@Component
public class SpringService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public String getProperties() {
        return applicationContext.getEnvironment().getProperty("feidao");
    }
}

在Application類實現ApplicationContextAware,通過applicationContext獲取SpringService類,實現如下:

package com.zjl.spring;

import com.zjl.spring.service.SpringService;
import org.springframework.beans.BeansException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@SpringBootApplication
public class Application implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.run(args);
        SpringService springService = getBean(SpringService.class);
        System.out.println(springService.getProperties());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> targetClz) {
        return applicationContext.getBean(targetClz);
    }

}

啓動spring,結果:打印出了run firstInitializer和feidao屬性的值zjl。
在這裏插入圖片描述

源碼

注入

spring.factories

Spring源碼之SpringFactoriesLoader(一)中我們已經知道配置在META-INF/spring.factories中的屬性會通過SpringFactoriesLoader加載到內存,在這裏我們就不詳細說明了。

springApplication

通過調用springApplication的addInitializers方法將實現類加入initializer的lis中,源碼如下:
在這裏插入圖片描述

application.properties

通過在application.properties文件中配置context.initializer.classes屬性加載,我們可以在工程中搜索該配置,看看哪裏用到了,如下圖
在這裏插入圖片描述
可以看到DelegatingApplicationContextInitializer類中用到了這個配置,而且這個類也實現了ApplicationContextInitializer接口,我們可以一起看看該類的initialize方法。
首先通過getInitializerClasses方法加載到所有的context.initializer.classes配置的類,
在這裏插入圖片描述
通過applyInitializerClasses實例化加載進來的類,並且循環調用
在這裏插入圖片描述
在這裏插入圖片描述
所以通過context.initializer.classes配置的ApplicationContextInitializer實現類會通過DelegatingApplicationContextInitializer加載並且循環調用。

調用

通過在自定義實現的FirstInitializer的initialize方法中打上斷點,查看調用鏈,如下圖所示。
在這裏插入圖片描述
根據調用鏈進行分析,入口在springApplication.run方法中的prepareContext方法進入。
在這裏插入圖片描述
在prepareContext方法中調用了applyInitializers方法
在這裏插入圖片描述
進入applyInitializers中,可以看到拿到了所有的initializer,進行循環調用initialize方法。
在這裏插入圖片描述

應用

ApplicationContextInitializer一般是給web應用做初始化使用,還有就是加載一些數據源,爲應用拉取外部的一些配置,例如Apollo(動態配置中心)的配置如果也想通過@Value註解加載,就可以實現一個ApplicationContextInitializer,在應用啓動時把配置從apollo拉取下來加載到spring中,如下是ApolloApplicationContextInitializer的實現
在這裏插入圖片描述

總結

通過分析ApplicationContextInitializer,我們可以知道通過ApplicationContextInitializer我們可以給容器做一些初始化工作或者加載一些外部的配置屬性。

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