美文网首页SpringBoot源码解析
SpringBoot系统初始化器

SpringBoot系统初始化器

作者: lanmi17 | 来源:发表于2020-06-26 13:12 被阅读0次

介绍

类名

org.springframework.context.ApplicationContextInitializer

Spring容器刷新之前执行的回调函数

作用

向Spring容器中注册属性(SpringBoot留的一个扩展点,用于添加自定义属性值)

实现方式

方式一


1. // 实现 ApplicationContextInitializer 接口
   // FirstInitializer.java
@Slf4j
@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("first","value1");
        MapPropertySource propertySource = new MapPropertySource("first",map);
        environment.getPropertySources().addLast(propertySource);
        log.info("first initializer done!");
    }
}
2. // resource 目录下新建 META-INF文件夹,然后新建spring.factories 文件(之后讲解原因)
 org.springframework.context.ApplicationContextInitializer=上边新建类FirstInitializer的全路径名

接下来采用 environment 验证:

    private final Environment environment;

    public InitializerController(Environment environment) {
        this.environment = environment;
    }

    @GetMapping("/{key}")
    public ResponseEntity<String> initializer(@PathVariable String key){
        try {
            log.info("属性:{},对应value为:{}",key,Optional.ofNullable(environment.getProperty(key)).orElse("属性不存在"));
        } catch (Exception e){
            e.printStackTrace();
        }
        return new ResponseEntity<>(HttpStatus.OK);
    }

输出结果:属性:first,对应value为:value1

方式二

采用硬编码的方式,在启动类主动注册

//  SecondInitializer.java
public class SecondInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map<String,Object> map = new HashMap<>();
        map.put("second","value2");
        MapPropertySource propertySource = new MapPropertySource("first",map);
        environment.getPropertySources().addLast(propertySource);
        log.info("second initializer done!");
    }
}
// 启动类
public class SbCodeApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SbCodeApplication.class);
        application.addInitializers(new SecondInitializer());
        application.run(args);
    }

}

使用方式1的验证,同样可以获取到预期的结果:属性:second,对应value为:value2

方式三

application.yml 文件中指定 context.initializer.classes : 自定义初始化器全路径,使用方式1的验证,同样可以获取到预期的结果:属性:third,对应value为:value3

方式对比

日志:

INFO 6364 --- [           main] c.l.s.code.Initializer.ThirdInitializer  : third initializer done!
INFO 6364 --- [           main] c.l.s.code.Initializer.FirstInitializer  : first initializer done!
INFO 6364 --- [           main] c.l.s.c.Initializer.SecondInitializer    : second initializer done!

第三种方式,在application.yml配置文件中指定自定义初始化器,优先级最高

原理

加载机制

Debug SpringApplication.java


SpringApplication.java
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        //  <1> 使用SpringFactoryLoader 获取所有的实现初始化器的类名
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // <2> 获取类对应的实例对象
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // <3> 排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

SpringFactoriesLoader

介绍:从classpath下多个jar包特定位置读取并初始化类,实现通用工厂类加载,实现扩展类加载
工作原理

// 对应以上<1>的过程
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        // 获取类名为 factoryType的所有实现类的String
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 从缓存获取
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            // 扫描classpath路径下所有的文件: "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();
                // 将spring.factories 转为UrlResource
                UrlResource resource = new UrlResource(url);
                // resource 转为可处理的kv对象
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    // 处理为 <factoryTypeName,List<实现类路径>>
                    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);
        }
    }
    // 对应以上 <2> 的过程
    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

通过以上<1><2><3> 获取所有实现初始化器的实例对象

调用时机

获取到实例对象以后,如何使用呢?Debug SpringApplication.run(SbCodeApplication.class,args) run方法

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
        new Class[] { ConfigurableApplicationContext.class }, context);
        // 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        // 加载初始化器实现
        applyInitializers(context);
        listeners.contextPrepared(context);
        
        ......
        }
// 循环遍历初始化器实现
protected void applyInitializers(ConfigurableApplicationContext context) {
    for (ApplicationContextInitializer initializer : getInitializers()) {
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        initializer.initialize(context);
    }
}

相关文章

网友评论

    本文标题:SpringBoot系统初始化器

    本文链接:https://www.haomeiwen.com/subject/anzyfktx.html