前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@Import注解的使用和原理

@Import注解的使用和原理

作者头像
Java进阶之路
发布2022-08-03 17:07:10
9590
发布2022-08-03 17:07:10
举报
文章被收录于专栏:分布式与微服务

一:简述

@Import注解是Spring中比较核心的注解,它的功能很强大,特别是在SpringBoot中,@Import注解使用的地方非常多,无论是@EnableXX类型的注解,还是自动装配都和@Import注解脱不开关系。今天就和大家聊聊它的作用以及原理。

二:@Import注解的作用

@Import功能和Spring XML配置文件里面的<Import>标签一样,也就是用来把配置类或者一些需要加载的类加入到Spring IoC容器中。

而导入的类可以分为三种情况:

  1. 导入@Configuration的配置类或普通类(在Spring4.2之后支持导入普通类)
代码语言:javascript
复制
@Component
@Import(User.class)
public class ImportTest {
    
}
代码语言:javascript
复制
@Data
public class User {

    private Integer id;

    private String username;

    private String password;
}

这种情况下,@Import注解的作用就是将类加入到IoC容器中

  1. 导入 ImportSelector接口或 DeferredImportSelector接口的实现类

ImportSelector接口:

代码语言:javascript
复制
public class ImportSeletorTest implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.User"};
    }
}
代码语言:javascript
复制
@Component
@Import(ImportSeletorTest.class)
public class ImportTest {

}
代码语言:javascript
复制
package com.example;

public class User {

    private Integer id;

    private String username;

    private String password;
}

DeferredImportSelector接口:

代码语言:javascript
复制
public class DeferredImportTest implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return null;
    }

    @Override
    public Class<? extends Group> getImportGroup() {
        return TestGroup.class;
    }

     private static class TestGroup implements Group {

        List<Entry> entries = new ArrayList<>();

        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            Entry entry = new Entry(metadata,"com.example.User");
            entries.add(entry);
        }

        @Override
        public Iterable<Entry> selectImports() {
            return entries;
        }
    }
}
代码语言:javascript
复制
package com.example;

public class User {

    private Integer id;

    private String username;

    private String password;
}

这种情况下我们可以自定义逻辑,根据我们的业务逻辑判断导入一些类到IoC容器中。

  1. 导入 ImportBeanDefinitionRegistrar接口的实现类
代码语言:javascript
复制
public class RegistrarTest implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("user",new RootBeanDefinition(User.class));
    }
}
代码语言:javascript
复制
@Component
@Import(RegistrarTest.class)
public class ImportTest {

}
代码语言:javascript
复制
package com.example;

public class User {

    private Integer id;

    private String username;

    private String password;
}

如果@Import注解导入的类是ImportBeanDefinitionRegistrar的实现类,那么可以利用registerBeanDefinitions()方法将bean注入到IoC容器中。

注:DeferredImportSelector接口是ImportSelector接口的子接口,它们的区别在下文进行分析

三:源码分析

在ConfigurationClassPostProcessor这个处理器中,它实现了BeanDefinitionRegistryPostProcessor接口,所以对bean初始化之前会调用postProcessBeanDefinitionRegistry()方法,在postProcessBeanDefinitionRegistry()方法中对@Component,@ComponentScan,@Import,@ImportResource等注解进行了处理。调用路径是这样的:

对于@Import注解的处理是在ConfigurationClassParser类的processImports()方法中,所以我们重点分析processImports()方法。

代码语言:javascript
复制
protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {
                        
          ...... 
                
                //由于篇幅原因前面代码省略 重点看processImports()方法
                
                //处理@Import注解
    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
                
                //省略
                .....
               
  }

流程图:

1. 如果是ImportSelector类型

a. 进一步判断是否是DeferredImportSelector类型,如果是,加入到deferredImportSelectors里面最后处理,在parse()方法,最后一行才调用deferredImportSelectorHandler.process()方法进行处理。最后会调用process()方法和group的electImports()获取到需要导入的类,然后再次调用processImports方法。

b. 如果不是DeferredImportSelector类型,那就调用selectImports方法,获取到所有的需要注入的类,这时再次调用processImports方法。

2. 如果是 ImportBeanDefinitionRegistrar 类型,这里也是 先实例一个对象,然后加入到 importBeanDefinitionRegistrars 里面,后续会在ConfigurationClassBeanDefinitionReader这个类里面的loadBeanDefinitionsFromRegistrars方法处理的

3. 如果既不是ImportSelector类型也不是ImportBeanDefinitionRegistrar类型,就再调用processConfigurationClass()方法进行处理。

源码:

代码语言:javascript
复制
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
      boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
      return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
      this.importStack.push(configClass);
      try {
        for (SourceClass candidate : importCandidates) {
          if (candidate.isAssignable(ImportSelector.class)) {
                                        //处理ImportSelector的实现类
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                this.environment, this.resourceLoader, this.registry);
            Predicate<String> selectorFilter = selector.getExclusionFilter();
            if (selectorFilter != null) {
              exclusionFilter = exclusionFilter.or(selectorFilter);
            }
                                                // 如果是DeferredImportSelector()类型调用deferredImportSelectorHandler的handle方法
            if (selector instanceof DeferredImportSelector) {
              this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            }
            else {
                                                       //调用selectImports()方法获取需要导入的类 排除需要排除的类之后 递归调用一下processImports
              String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
              Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
              processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
          }
                                        // 如果是导入实现了ImportBeanDefinitionRegistrar的类
          else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                    this.environment, this.resourceLoader, this.registry);
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
          }
          else {
            this.importStack.registerImport(
                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                                                //对于配置类和普通类调用processConfigurationClass进行处理
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
          }
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to process import candidates for configuration class [" +
            configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
        this.importStack.pop();
      }
    }
  }

四:DeferredImportSelector和ImportSelector的区别

根据前文我们已经知道了DeferredImportSelector是ImportSelector的子接口,它们二者最主要的区别在于DeferredImportSelector接口的实现类会在最后进行处理(在@Bean,@ImportResource等注解之后)。在processImports()方法处理DeferredImportSelector方法的时候会调用 deferredImportSelectorHandler.handle()进行处理。我们可以看到deferredImportSelectors在类中已经new了,所以第一次进来肯定不为null,所以只会先放入到deferredImportSelectors,等之后处理。

代码语言:javascript
复制
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
      DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
      //第一次进来肯定不为null 只有在调用deferredImportSelectorHandler.process()之后才会为null
      if (this.deferredImportSelectors == null) {
        DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
        handler.register(holder);
        handler.processGroupImports();
      }
      else {
                                //放入到deferredImportSelectors中
        this.deferredImportSelectors.add(holder);
      }
    }

在parse()方法中,我们可以明显看到DeferredImportSelector是最后处理。

代码语言:javascript
复制
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
        if (bd instanceof AnnotatedBeanDefinition) {
          parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        }
        else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
          parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
        }
        else {
          parse(bd.getBeanClassName(), holder.getBeanName());
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
    }

    //最后处理deferredImportSelector导入的类
    this.deferredImportSelectorHandler.process();
  }

最后分析下DeferredImportSelector类的处理,它首先会获取DeferredImportSelector类的getImportGroup()返回的Group,如果返回的Group是null(默认实现是返回null),就会使用默认的Group(也就是DefaultDeferredImportSelectorGroup),然后会分别调用Group的process()方法和selectImports()方法收集需要导入的类,最后会调用processImports()方法将收集的类导入到Spring容器中。

注:SpringBoot的自动装配和DeferredImportSelector类是脱不开关系的,我们理解了DeferredImportSelector,那么自动装配的原理也就懂了一大半了。

源码:

deferredImportSelectorHandler的process()方法负责处理导入DeferredImportSelector类的处理逻辑,首先循环收集的DeferredImportSelectorHolder,调用它的register方法将DeferredImportSelector进行分组,然后调用processGroupImports()方法分组进行处理。

process()

代码语言:javascript
复制
public void process() {
      List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
      //将deferredImportSelectors设置为null 说明已经到了进行deferredImportSelectors处理的阶段了
      // 下次就不需要在延迟处理了
      this.deferredImportSelectors = null;
      try {
        if (deferredImports != null) {
          DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
          deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
          //循环调用register方法
          deferredImports.forEach(handler::register);
          handler.processGroupImports();
        }
      }
      finally {
        this.deferredImportSelectors = new ArrayList<>();
      }
    }
  }

register()

register()方法的作用是根据DeferredImportSelector的getImportSelector()方法将收集的DeferredImportSelector进行分组,并且保存在DeferredImportSelectorGrouping中。

代码语言:javascript
复制
public void register(DeferredImportSelectorHolder deferredImport) {
      //获取Group
      Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
      //如果getImportGroup()方法返回的Group为null 那么就以DeferredImportSelectorHolder为key 并且用默认的DeferredImportSelectorGrouping为value
      DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
          (group != null ? group : deferredImport),
          key -> new DeferredImportSelectorGrouping(createGroup(group)));
      grouping.add(deferredImport);
      //将需要导入的类加入到configurationClasses中
      this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
          deferredImport.getConfigurationClass());
    }

processGroupImports()

processGroupImports()方法作用是根据getImports()方法获取到需要导入的类,然后再次通过processImports()方法将需要导入的类导入到Spring容器中。

代码语言:javascript
复制
public void processGroupImports() {
      for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        //循环getImports()返回的Entry 获取需要导入的类
        grouping.getImports().forEach(entry -> {
          ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
          try {
            //将需要导入的类再次通过processImports()方法进行处理
            processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                exclusionFilter, false);
          }
          catch (BeanDefinitionStoreException ex) {
            throw ex;
          }
          catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                "Failed to process import candidates for configuration class [" +
                    configurationClass.getMetadata().getClassName() + "]", ex);
          }
        });
      }
    }

getImports()

getImports()方法通过Group的process和selectImports()方法收集需要导入的类。

代码语言:javascript
复制
public Iterable<Group.Entry> getImports() {
      //循环所有的deferredImports 调用Group的process()方法
      for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
      }
      //调用Group的selectImports() 并且返回收集到的Entry
      return this.group.selectImports();
    }

默认的Group:

DefaultDeferredImportSelectorGroup

代码语言:javascript
复制
private static class DefaultDeferredImportSelectorGroup implements Group {

    private final List<Entry> imports = new ArrayList<>();

    @Override
    public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
                        //调用ImportSelector的selectImports()方法收集需要导入的类
      for (String importClassName : selector.selectImports(metadata)) {
        this.imports.add(new Entry(metadata, importClassName));
      }
    }

    @Override
    public Iterable<Entry> selectImports() {
      return this.imports;
    }
  } 
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java进阶之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档