• Spring源码阅读 @Import 一个普通类


    1. 解析逻辑

    具体逻辑在 org.springframework.context.annotation.ConfigurationClassParser#processImports 中,下面的 importCandidates 即为 @Import 导入的类的列表。
    最后的一个 else 即为普通类的处理逻辑,实际上就是将其当做一个配置类进行处理。
    同时要注意到,在这里为止,@Import 导入的类还没有变为 BD 被注入容器,那么这个时机在哪

    for (SourceClass candidate : importCandidates) {
        // Import 的类继承了 ImportSelector 接口
        if (candidate.isAssignable(ImportSelector.class)) {
            // ...
        }
        // 继承了 ImportBeanDefinitionRegistrar 接口,说明这个类有想自己向容器注入 BD 的想法,比如说 MyBatis 自己收集接口, 自己注入这些接口代理类的 BD
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // ........
        }
        else {
            // 普通类,这里的普通类包括 @Import 直接导入的没有继承上面三个接口的类,还有就是 @Import 导入了继承 ImportSelector 接口的类要注入的普通类
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            this.importStack.registerImport(
                    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            // 把这个"普通类"当做配置类处理
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
    

    2. 注入时机

    从上面可见,被 @Import 的这些普通类并未被直接转换 BD 注入容器,仅是将这些类当做配置类处理一遍。
    调用的处理配置类的方法为:org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass

    protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    	// ... 略
        SourceClass sourceClass = asSourceClass(configClass, filter);
        do {
            // 这个是处理父子类的问题,一般注入子类,那么后面会查看是否有父类,父类会返回为 sourceClass,再解析 sourceClass
            //    但是配置类还是认为是最初的子类 configClass
            sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
        }
        while (sourceClass != null);
    
        this.configurationClasses.put(configClass, configClass);
    }
    

    可以看到这些被 @Import 的普通类虽然暂时没有被转换 BD 注入容器,但是被加入了一个集合。
    下面转换到 ConfigurationClassPostProcessor 类中来,
    方法:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

    // ... 略
    do {
        // 这里是配置类最初始的调用解析
        parser.parse(candidates);
        parser.validate();
    
        // 这里获取的就是上面那个集合
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        // 去除已经解析过的,这个实际主要是用在后面 loadBeanDefinitions 去除不必要的 BD, removeAll 是为了减少判断
        //  alreadyParsed 是上一次循环已经解析过的,去除上一次解析过的就是这一次解析过的,主要是 Parser 是复用的,所以需要 removeAll
        configClasses.removeAll(alreadyParsed);
    
        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 这里, 内部就讲这些 @Import 的普通类给注入容器了
        this.reader.loadBeanDefinitions(configClasses);
    }
    

    3. 注入逻辑

    待续

  • 相关阅读:
    原型和原型链
    全局对象与临时转换
    JavaScript的数据类型转换
    JavaScript 定时任务多事件冲突问题
    PHP 5.5.38 + mysql 5.0.11 + zabbix3.0 + nginx 安装
    mysql 查找多个值并且取最大值一个和分组
    mysql 关联左表不存在数据 并 根据身份证计算查找大于65岁以上老人
    JavaScript for 循环累加 json 字符串
    jQuery.1.9 live 代替事件 on 新增内容无法触发事件
    MySQL 字段基本操作
  • 原文地址:https://www.cnblogs.com/chenxingyang/p/16123999.html
Copyright © 2020-2023  润新知