SpringBoot源码课程学习总结—1、初始化器

267 阅读3分钟

慕课网SpringBoot源码教程学习总结

一、系统初始化器

类名:ApplicationContextInitializer

介绍:Spring容器刷新之前执行的一个回调函数

作用:向SpringBoot容器中注册属性

使用:继承接口自定义实现

1、自定义初始化器

实现ApplicationContextInitializer接口

@Order(1)
public class FirstInitializer implements ApplicationContextInitializer {
 
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key1", "value1");
        MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("run firstInitializer");
    }
}

向容器注入初始化器:

  1. 在resources下新建META-INF/spring.factories文件,添加配置

    org.springframework.context.ApplicationContextInitializer=cn.nupt.sb.initializer.FirstInitializer
    
  2. 修改Application启动类

    public static void main(String[] args) {
        //        SpringApplication.run(SpringbootApplication.class, args);
        SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
        springApplication.addInitializers(new SecondInitializer());
        springApplication.run(args);
    }
    
  3. 修改application.properties文件

    context.initializer.classes=cn.nupt.sb.initializer.ThirdInitializer
    

2、工厂加载机制解析

SpringFactoriesLoader介绍

  • 框架内部使用的通用工厂加载机制
  • 从classpath下多个jar包特定的位置读取文件并初始化类
  • 文件内容必须是kv形式
  • key是全限定名(抽象类|接口),value是实现,多个实现使用,分隔

SpringFactoriesLoader作用

  • SpringBoot框架从类路径jar包中读取特定文件实现扩展类的载入

loadFactories流程

(1)启动类执行

(2)查找缓存,存在返回

(3)不存在,读取指定资源文件

(4)构造properties对象

(5)获取指定key对应value值

(6)逗号分隔value

(7)保存结果到缓存

(8)依次实例化结果对象

(9)对结果对象排序

(10)返回

SpringFactoriesLoader.png

源码解析:

进入springboot启动类

SpringApplication.run(SpringbootApplication.class, args);

进入run方法,在SpringApplication初始化器方法中获取初始化器:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //设置系统初始化器以及自定义初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

进入getSpringFactoriesInstances方法

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    //通过SpringFactoriesLoader.loadFactoryNames方法获取所有初始化器类名
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //通过反射方式获取初始化器实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    //根据初始化器设置的order进行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

进入SpringFactoriesLoader.loadFactoryNames方法:

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //查找缓存,如果存在key为classLoader的值,直接返回
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
 
    try {
        //读取指定资源文件,即META-INF/spring.factories文件
        Enumeration<URL> urls = (classLoader != null ?
                                 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

3、系统初始化器解析

作用:

  • 上下文刷新即refresh方法前调用
  • 用来编码设置一些属性变量通常用在web环境中
  • 可以通过order接口进行排序

调用流程:

run()框架启动--》prepareContext()上下文准备--》applyInitializers()调用系统初始化器--》遍历调用初始化器

实现原理:

  • 定义在spring.factories文件中被SpringfactoriesLoader发现注册
  • SpringApplication初始化完毕后手动添加
  • 定义成环境变量被DelegatingApplicationContextInitializer发现注册

4、面试题

(1)介绍下SpringFactoriesLoader?

答:SpringFactoriesLoader是SpringBoot提供的通用工厂加载机制,用于实现扩展类的载入。

(2)SpringFactoriesLoader如何加载工厂类?

答:从classpath下多个jar包特定的位置读取文件并初始化类,将kv形式的内容读取为Properties,然后依次进行遍历,排序。

(3)系统初始化器的作用?

答:SpringBoot容器的一个回调接口,通过它向容器定义属性。

(4)系统初始化器调用时机?

答:是在SpringBoot启动时的run方法中的prepareContex方法中调用的。

(5)如何自定义实现系统初始化器?

答:三种方式,(1)Application启动类中添加(2)spring.factories文件中添加配置(3)application.properties文件添加配置。

(6)自定义实现系统初始化器有哪些注意事项?

答:order排序和失效问题。使用第三种方式时,通过context.initializer.classes配置的初始化器是由DelegatingApplicationContextInitializer调用的,而该系统初始化器则是通过SpringFactoriesLoader加载的,其order为0,最先执行initialize方法,在该方法内,按照order注解值排序,然后逐个调用initialize方法。因此通过context.initializer.classes配置的初始化器是最先加载的。

private static final String PROPERTY_NAME = "context.initializer.classes";

private int order = 0;

@Override
public void initialize(ConfigurableApplicationContext context) {
    ConfigurableEnvironment environment = context.getEnvironment();
    List<Class<?>> initializerClasses = getInitializerClasses(environment);
    if (!initializerClasses.isEmpty()) {
        applyInitializerClasses(context, initializerClasses);
    }
}

private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
    //获取context.initializer.classes属性值
    String classNames = env.getProperty(PROPERTY_NAME);
    List<Class<?>> classes = new ArrayList<>();
    if (StringUtils.hasLength(classNames)) {
        for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
            //获取初始化器Class对象
            classes.add(getInitializerClass(className));
        }
    }
    return classes;
}

private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
    Class<?> contextClass = context.getClass();
    List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
    for (Class<?> initializerClass : initializerClasses) {
        //通过反射方式实例化初始化器
        initializers.add(instantiateInitializer(contextClass, initializerClass));
    }
    //按照order注解值对初始化器排序,并依次调用ApplicationContextInitializer接口实现方法
    applyInitializers(context, initializers);
}