SpringBoot——初始化器解析

初始化器

Spring 是一個擴(kuò)展性很強(qiáng)的容器框架,為開發(fā)者提供了豐富的擴(kuò)展入口,其中一個擴(kuò)展點(diǎn)便是 ApplicationContextInitializer (應(yīng)用上下文初始化器 )。

ApplicationContextInitializer 是 Spring 在執(zhí)行 ConfigurableApplicationContext.refresh() 方法對應(yīng)用上下文進(jìn)行刷新之前調(diào)用的一個回調(diào)接口,用來完成對 Spring 應(yīng)用上下文個性化的初始化工作,該接口定義在 org.springframework.context 包中,其內(nèi)部僅包含一個 initialize() 方法,其定義代碼如下。

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

    /**
     * Initialize the given application context.
     * @param applicationContext the application to configure
     */
    void initialize(C applicationContext);

}

自定義初始化器

在 Springboot 中使用自定義初始化器大致可以分為以下兩個步驟:

  • 自定義初始化器,一般是實(shí)現(xiàn) ApplicationContextInitializer 接口。
  • 注冊初始化器。

第一步:自定義初始化器,此處為了測試初始化器的執(zhí)行順序定義了如下3個初始化器

@Order(1)
public class Initializer1 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map<String,Object> map = new HashMap<>();
        map.put("key1","value1");
        MapPropertySource mapPropertySource = new MapPropertySource("Initializer1", map);
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("run Initializer1");
    }
}
@Order(2)
public class Initializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map<String,Object> map = new HashMap<>();
        map.put("key2","value2");
        MapPropertySource mapPropertySource = new MapPropertySource("Initializer2", map);
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("run Initializer2");
    }
}
@Order(3)
public class Initializer3 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map<String,Object> map = new HashMap<>();
        map.put("key3","value3");
        MapPropertySource mapPropertySource = new MapPropertySource("Initializer3", map);
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("run Initializer3");
    }
}

第二步:注冊初始化器,有以下三種方式

  • 方式一:在啟動類中,使用 SpringApplication.addInitializers() 方法注冊。
@SpringBootApplication
@MapperScan("com.yibo.source.code.mapper")//掃描Mapper接口
public class Application {

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.addInitializers(new Initializer2());
        springApplication.run();
    }
}
  • 方式二:在 Springboot 核心配置文件 application.properties 中增加 context.initializer.classes = [ 初始化器全類名 ] 進(jìn)行注冊。
context.initializer.classes=com.yibo.source.code.initializer.Initializer3
  • 方式三:通過在CLASSPATH/META-INF/spring.factories中添加 org.springframework.context.ApplicationContextInitializer 配置項(xiàng)進(jìn)行注冊。
org.springframework.context.ApplicationContextInitializer=com.yibo.source.code.initializer.Initializer1

注意:雖然可以使用 @Order 注解來控制多個初始化器的執(zhí)行順序(數(shù)值越小越先執(zhí)行),但是,通過不同方式注冊的初始化器的執(zhí)行順序也有所不同,若多個初始化器注冊的方式不同會導(dǎo)致 @Order 注解順序無效,從以上程序執(zhí)行后的打印結(jié)果來看,三種方式注冊的初始化器的執(zhí)行順序依次是:方式二 --> 方式一 --> 方式三。

Springboot定義的初始化器

Springboot定義的 ApplicationContextInitializer 接口的實(shí)現(xiàn)類有下面幾個,如圖所示。

DelegatingApplicationContextInitializer

DelegatingApplicationContextInitializer 初始化器負(fù)責(zé)讀取核心配置文件 context.initializer.classes 配置項(xiàng)指定的初始化器,并調(diào)用它們的 initialize() 方法來完成對應(yīng)用上下文的初始化工作。

public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    private static final String PROPERTY_NAME = "context.initializer.classes";
    private int order = 0;

    public DelegatingApplicationContextInitializer() {
    }

    /**
     * 對應(yīng)用上下文進(jìn)行初始化
     */
    public void initialize(ConfigurableApplicationContext context) {
        // 獲取核心配置文件中指定的初始化器類
        ConfigurableEnvironment environment = context.getEnvironment();
        List<Class<?>> initializerClasses = this.getInitializerClasses(environment);
        if (!initializerClasses.isEmpty()) {
            // 利用獲取到的初始化器類對應(yīng)用上下文進(jìn)行初始化
            this.applyInitializerClasses(context, initializerClasses);
        }

    }

    /**
     * 讀取核心配置文件中 context.initializer.classes 指定的初始化器類
     */
    private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
        String classNames = env.getProperty("context.initializer.classes");
        List<Class<?>> classes = new ArrayList();
        if (StringUtils.hasLength(classNames)) {
            String[] var4 = StringUtils.tokenizeToStringArray(classNames, ",");
            int var5 = var4.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String className = var4[var6];
                classes.add(this.getInitializerClass(className));
            }
        }

        return classes;
    }

    /**
     * 使用指定的初始化器類對應(yīng)用上下文進(jìn)行初始化
     */
    private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
        Class<?> contextClass = context.getClass();
        List<ApplicationContextInitializer<?>> initializers = new ArrayList();
        Iterator var5 = initializerClasses.iterator();

        while(var5.hasNext()) {
            Class<?> initializerClass = (Class)var5.next();
            initializers.add(this.instantiateInitializer(contextClass, initializerClass));
        }

        this.applyInitializers(context, initializers);
    }


    /**
     * 使用指定的初始化器對應(yīng)用上下文進(jìn)行初始化
     */
    private void applyInitializers(ConfigurableApplicationContext context, List<ApplicationContextInitializer<?>> initializers) {
        // 對初始化器進(jìn)行 Order 排序
        initializers.sort(new AnnotationAwareOrderComparator());
        Iterator var3 = initializers.iterator();

        while(var3.hasNext()) {
            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var3.next();
            initializer.initialize(context);
        }

    }
    ......
}

ContextIdApplicationContextInitializer

ContextIdApplicationContextInitializer 初始化器的作用是給應(yīng)用上下文設(shè)置一個ID
這個ID通過environment中的"spring.application.name"獲取,如果不存在則直接返回默認(rèn)"application"

public class ContextIdApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    private int order = 2147483637;

    public ContextIdApplicationContextInitializer() {
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public int getOrder() {
        return this.order;
    }

    public void initialize(ConfigurableApplicationContext applicationContext) {
        ContextIdApplicationContextInitializer.ContextId contextId = this.getContextId(applicationContext);
        applicationContext.setId(contextId.getId());
        applicationContext.getBeanFactory().registerSingleton(ContextIdApplicationContextInitializer.ContextId.class.getName(), contextId);
    }

    private String getApplicationId(ConfigurableEnvironment environment) {
        String name = environment.getProperty("spring.application.name");
        return StringUtils.hasText(name) ? name : "application";
    }
}

ConfigurationWarningsApplicationContextInitializer

ConfigurationWarningsApplicationContextInitializer 初始化器用來對常見的由于配置錯誤而引起的警告進(jìn)行打印報告。

public class ConfigurationWarningsApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    private static final Log logger = LogFactory.getLog(ConfigurationWarningsApplicationContextInitializer.class);

    public void initialize(ConfigurableApplicationContext context) {
        // 添加一個 ConfigurationWarningsPostProcessor 用來打印警告信息
        context.addBeanFactoryPostProcessor(new ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(this.getChecks()));
    }
}

其中的 ConfigurationWarningsPostProcessor 是一個靜態(tài)內(nèi)部類,用來打印注冊 BeanDefinition 過程中產(chǎn)生的配置錯誤警告信息。

protected static final class ConfigurationWarningsPostProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
    private ConfigurationWarningsApplicationContextInitializer.Check[] checks;

    public ConfigurationWarningsPostProcessor(ConfigurationWarningsApplicationContextInitializer.Check[] checks) {
        this.checks = checks;
    }

    public int getOrder() {
        return 2147483646;
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ConfigurationWarningsApplicationContextInitializer.Check[] var2 = this.checks;
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            ConfigurationWarningsApplicationContextInitializer.Check check = var2[var4];
            String message = check.getWarning(registry);
            if (StringUtils.hasLength(message)) {
                this.warn(message);
            }
        }

    }

    private void warn(String message) {
        if (ConfigurationWarningsApplicationContextInitializer.logger.isWarnEnabled()) {
            ConfigurationWarningsApplicationContextInitializer.logger.warn(String.format("%n%n** WARNING ** : %s%n%n", message));
        }

    }
}

ServerPortInfoApplicationContextInitializer

ServerPortInfoApplicationContextInitializer 初始化器通過監(jiān)聽 EmbeddedServletContainerInitializedEvent 事件,來對內(nèi)部服務(wù)器實(shí)際要監(jiān)聽的端口號進(jìn)行屬性設(shè)置。

public class ServerPortInfoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent> {
    public ServerPortInfoApplicationContextInitializer() {
    }

    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addApplicationListener(this);
    }

    public void onApplicationEvent(WebServerInitializedEvent event) {
        String propertyName = "local." + this.getName(event.getApplicationContext()) + ".port";
        this.setPortProperty((ApplicationContext)event.getApplicationContext(), propertyName, event.getWebServer().getPort());
    }

    private String getName(WebServerApplicationContext context) {
        String name = context.getServerNamespace();
        return StringUtils.hasText(name) ? name : "server";
    }

    private void setPortProperty(ApplicationContext context, String propertyName, int port) {
        if (context instanceof ConfigurableApplicationContext) {
            this.setPortProperty(((ConfigurableApplicationContext)context).getEnvironment(), propertyName, port);
        }

        if (context.getParent() != null) {
            this.setPortProperty(context.getParent(), propertyName, port);
        }

    }

    private void setPortProperty(ConfigurableEnvironment environment, String propertyName, int port) {
        MutablePropertySources sources = environment.getPropertySources();
        PropertySource<?> source = sources.get("server.ports");
        if (source == null) {
            source = new MapPropertySource("server.ports", new HashMap());
            sources.addFirst((PropertySource)source);
        }

        ((Map)((PropertySource)source).getSource()).put(propertyName, port);
    }
}

SharedMetadataReaderFactoryContextInitializer

SharedMetadataReaderFactoryContextInitializer 初始化器用來創(chuàng)建一個可以在 ConfigurationClassPostProcessor 和Spring Boot 之間共享的CachingMetadataReaderFactory。

class SharedMetadataReaderFactoryContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    public static final String BEAN_NAME = "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory";

    SharedMetadataReaderFactoryContextInitializer() {
    }

    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor());
    }
}

看下SpringBoot是如何加載初始化器的

入口類

@SpringBootApplication
@MapperScan("com.yibo.source.code.mapper")//掃描Mapper接口
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

@SpringBootApplication我們上一篇文章http://www.itdecent.cn/p/ba68748f8113
中大概的講過了,有興趣的可以看看我第一篇關(guān)于SpringBoot的文章,本篇文章主要關(guān)注SpringApplication.run(Application.class, args);,我們跟進(jìn)去看看

// 調(diào)用靜態(tài)類,參數(shù)對應(yīng)的就是HelloWorldMainApplication.class以及main方法中的args
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

它實(shí)際上會構(gòu)造一個SpringApplication的實(shí)例,并把我們的啟動類Application.class作為參數(shù)傳進(jìn)去,然后運(yùn)行它的run方法
SpringApplication構(gòu)造器

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //把Application.class設(shè)置為屬性存儲起來
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //設(shè)置應(yīng)用類型是Standard還是Web
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //設(shè)置初始化器(Initializer),最后會調(diào)用這些初始化器
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //設(shè)置監(jiān)聽器(Listener)
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

設(shè)置應(yīng)用類型

public enum WebApplicationType {
    NONE,
    SERVLET,
    REACTIVE;

    private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

    private WebApplicationType() {
    }

    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }
}

這里主要是通過類加載器判斷REACTIVE相關(guān)的Class是否存在,如果不存在,則web環(huán)境即為SERVLET類型。這里設(shè)置好web環(huán)境類型,在后面會根據(jù)類型初始化對應(yīng)環(huán)境。大家還記得我們第一篇文章中引入的依賴嗎?

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-web 的pom又會引入Tomcat和spring-webmvc,如下

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.10.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.1.10.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.1.10.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.18.Final</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.11.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.11.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

很明顯spring-webmvc中存在DispatcherServlet這個類,也就是我們以前SpringMvc的核心Servlet,通過類加載能加載DispatcherServlet這個類,那么我們的應(yīng)用類型自然就是WebApplicationType.SERVLET

設(shè)置初始化器(Initializer)

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

我們先來看看this.getSpringFactoriesInstances(ApplicationContextInitializer.class)

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

// 這里的入?yún)ype就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = this.getClassLoader();
    // 使用Set保存names來避免重復(fù)元素
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根據(jù)names來進(jìn)行實(shí)例化
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    // 對實(shí)例進(jìn)行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

這里面首先會根據(jù)入?yún)ype讀取所有的names(是一個String集合),然后根據(jù)這個集合來完成對應(yīng)的實(shí)例化操作:

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    // 入?yún)⒕褪茿pplicationContextInitializer.class
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                //從類路徑的META-INF/spring.factories中加載所有默認(rèn)的自動配置類
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        //獲取ApplicationContextInitializer.class的所有值
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }
}

這個方法會嘗試從類路徑的META-INF/spring.factories處讀取相應(yīng)配置文件,然后進(jìn)行遍歷,讀取配置文件中Key為:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure這個包為例,它的META-INF/spring.factories部分定義如下所示:


# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

這兩個類名會被讀取出來,然后放入到Set<String>集合中,準(zhǔn)備開始下面的實(shí)例化操作:

// parameterTypes: 上一步得到的names集合
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList(names.size());
    Iterator var7 = names.iterator();

    while(var7.hasNext()) {
        String name = (String)var7.next();

        try {
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            //確認(rèn)被加載類是ApplicationContextInitializer的子類
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            //反射實(shí)例化對象
            T instance = BeanUtils.instantiateClass(constructor, args);
            //加入List集合中
            instances.add(instance);
        } catch (Throwable var12) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
        }
    }

    return instances;
}

確認(rèn)被加載的類確實(shí)是org.springframework.context.ApplicationContextInitializer的子類,然后就是得到構(gòu)造器進(jìn)行初始化,最后放入到實(shí)例列表中。

因此,所謂的初始化器就是org.springframework.context.ApplicationContextInitializer的實(shí)現(xiàn)類,這個接口是這樣定義的:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

    /**
     * Initialize the given application context.
     * @param applicationContext the application to configure
     */
    void initialize(C applicationContext);

}

在Spring上下文被刷新之前進(jìn)行初始化的操作。即在SpringApplication.prepareContext方法中調(diào)用applyInitializers方法進(jìn)行初始化操作,典型地比如在Web應(yīng)用中,注冊Property Sources或者是激活Profiles。Property Sources比較好理解,就是配置文件。Profiles是Spring為了在不同環(huán)境下(如DEV,TEST,PRODUCTION等),加載不同的配置項(xiàng)而抽象出來的一個實(shí)體。

public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        ......
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        ......
    }
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        ......
        this.applyInitializers(context);
        ......

    }
}

SpringFactoriesLoader介紹

  • 1、框架內(nèi)部使用的通用工廠加載機(jī)制
  • 2、從classpath下多個jar包特定的位置讀取文件并初始化類
  • 3、文件內(nèi)容必須是kv形式,即properties類型
  • 4、key是全限定名,value是實(shí)現(xiàn),多個實(shí)現(xiàn)用逗號分隔

SpringFactoriesLoader的作用

SpringBoot框架中從類路徑j(luò)ar包中讀取特定文件實(shí)現(xiàn)擴(kuò)展類的載入


系統(tǒng)初始化器調(diào)用流程

文章開始的三種自定義初始化器實(shí)現(xiàn)原理

  • 定義在spring.factories文件中被SpringFactoriesLoader發(fā)現(xiàn)注冊
  • SpringApplication初始化完畢后手動添加
  • 定義成環(huán)境變量被DelegatingApplicationContextInitializer發(fā)現(xiàn)注冊

推薦使用在spring.factories文件中定義系統(tǒng)初始化器被SpringFactoriesLoader發(fā)現(xiàn)注冊的方式

在spring.factories文件中定義系統(tǒng)初始化器,被SpringFactoriesLoader載入,成為SpringBoot框架的SpringApplicationInitializer屬性,在運(yùn)行的時候會遍歷這個屬性依次調(diào)用initialize方法完成向容器內(nèi)注冊屬性。


參考:
https://blog.csdn.net/pengjunlee/article/details/79394735

https://www.cnblogs.com/java-chen-hao/p/11829344.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • springboot 概述 SpringBoot能夠快速開發(fā),簡化部署,適用于微服務(wù) 參考嘟嘟大神SpringBo...
    一紙硯白閱讀 5,741評論 2 20
  • 一、Springboot簡介 1.1 Springboot是什么 Spring Boot就是Spring,它做了那...
    這一刻_776b閱讀 559評論 0 0
  • 多線程實(shí)現(xiàn)方案 GCD的隊(duì)列類型 并發(fā)隊(duì)列自己創(chuàng)建的全局 串行隊(duì)列主隊(duì)列自己創(chuàng)建的 NSOperationQueu...
    Style_偉閱讀 179評論 0 0
  • 今天中午,我們吃完中午餐了,去爬白石山,白石山還是我們南區(qū)的一個旅游景點(diǎn),我覺得很多人都聽說過吧!但是我長這么...
    德麗人閱讀 741評論 2 0
  • 敬愛的李老師,智慧的班主任,親愛的學(xué)兄們: 大家好!我是領(lǐng)航商貿(mào)有限公司的汪艷,今天(2019.6.30)是我日精...
    領(lǐng)航商貿(mào)閱讀 75評論 0 0

友情鏈接更多精彩內(nèi)容