Spring框架解析之@Import

​本文是Spring IoC容器技术介绍系列文章之一。本文介绍Spring IoC中的@Import注解。

功能解释

@Import annotation allows for loading @Bean definitions from another configuration class

@Import注解支持从其他的配置类中加载@Bean的定义。

一般通过在程序中使用配置类的方式定义所有的bean对象,但当我们希望使用工程中引入的其他包的配置类时,@Import注解就非常有用了。它可以帮助我们在不改变外部包的同时,引入别人定义好的配置类,进而加载这些配置类里面的bean对象。

使用示例

@Import(AnotherConfiguration.class)
public class Application {
    static class AnotherConfiguration {
        @Bean NumberStyleFormatter numberStyleFormatter() {
            return new NumberStyleFormatter();
        }
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
        AnotherConfiguration configuration = applicationContext.getBean(AnotherConfiguration.class);
        NumberStyleFormatter formatter = applicationContext.getBean(NumberStyleFormatter.class);
        System.out.println(configuration.getClass().getName());
        System.out.println(formatter.getClass().getName());
    }
}

上面这段代码展示了如何使用@Import注解定义bean对象。

Application类中定义了一个名为AnotherConfiguration的内部类。定义成内部类的原因是为了代码长度尽可能短,便于展示。@Import引入的类,不需要非是当前类的内部类,也不需要在当前类的相同package下。

AnotherConfiguration中定义了一个NumberStyleFormatter类型的@Bean

Application上,使用了@Import(AnotherConfiguration.class)注解。

main()函数中,在容器启动后拿到AnotherConfiguration类型和NumberStyleFormatter类型的bean,并打印他们的名字。

程序执行情况

ImportSelector

ImportSelector是Spring Context中定义的接口。该接口有两个方法:

  • String[] selectImports(...):返回需要引入的配置类的名称数组。

  • Predicate<String> getExclusionFilter():返回哪些配置类需要过滤掉。该方法的返回是Predicate<String>对象。Predicate<String>是一个接收String类型参数作为入参,返回Boolean类型结果的方法。

ImportSelector继承图

如果@Import注解引入的对象是一个ImportSelector类型的类的话,则该类的String[] selectImports(...)方法最终会被调用,这个方法返回的String类型数组中的每一个元素,都会作为配置类被加载。

另外,该类的Predicate<String> getExclusionFilter()方法返回的Predicate<String>会被作为过滤函数。在该类引入的所有配置类进行解析时,使用它对这些配置类进行过滤。

ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar是在Spring Context中定义的,另一个和@Import配合使用,用于初始化IoC容器的接口。

该接口中定义了两个registerBeanDefinitions(...)方法。IoC容器在初始化时,如果@Import引入的对象是ImportBeanDefinitionRegistrar的某个具体子类,则registerBeanDefinitions(...)方法会被调起。registerBeanDefinitions(...)方法会将相关的@Bean定义写入到BeanDefinitionRegistry类型的入参中。

ImportBeanDefinitionRegistrar继承链

registerBeanDefinitions(...)方法中传入的BeanDefinitionRegistry类型的入参,其实就是ApplicationContext对象内部的beanFactory属性。这个beanFactory属性的类型是DefaultListableBeanFactory,而DefaultListableBeanFactory实现了BeanDefinitionRegistry接口。

BeanDefinitionRegistry的继承链

源码解释

@Import注解的处理在ConfigurationClassParser类中。ConfigurationClassParser是Spring框架用于处理配置类的核心对象,processImports(...)方法是处理@Import注解的核心方法:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
  ...
  if (...) {
   ...
  }
  else {
   ...
   try {
    for (SourceClass candidate : importCandidates) {
     if (candidate.isAssignable(ImportSelector.class)) {
      ...
      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);
      }
      if (selector instanceof DeferredImportSelector) {
       this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
      }
      else {
       ...
       processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
      }
     }
     else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
      ....
      configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
     }
     else {
      ...
      processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
     }
    }
   }
   catch (...) {
    ...
   }
   ...
  }
 }

processImports(...)中,主要根据candidate的类型的不同,做不一样的操作处理,具体的处理逻辑可以参考上面章节的相关介绍。

总结

@Import是Spring中用于引入其他配置类的注解,主要分为三种场景:

  • 引入配置类是ImportSelector类型的对象,则调用其selectImports(...)方法,得到一组配置类的清单,再对清单中的每个配置类分别处理,加载相关@Bean定义;

  • 引入配置类是ImportBeanDefinitionRegistrar类型的对象,则调用其registerBeanDefinitions(...)方法,完成@Bean定义的处理;

  • 其他类型的对象,则直接作为正常配置类解析,加载相关@Bean定义。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

镜悬xhs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值