在 Spring 中,使用 <import> 标签或者 @Import 注解能够向容器中导入配置类(使用 @Configuration 注解的类),利用这个功能可以很方便地实现模块化配置的效果。从 Spring 官方文档给出的说明了解到,在 Spring 4.2 版本以前,@Import 注解只支持导入配置类,在 Spring 4.2 及以后的版本,该注解支持导入普通的 Java 类。
 
1 2 3 4 5 6 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public  @interface  Import {    Class<?>[] value(); } 
 
可以看出,@Import 注解可以放在类、接口、注解和枚举上,我们简单写点代码来试验一下。
1 2 3 4 5 6 7 @Configuration public  class  ConfigA  {    @Bean      public  A getA ()  {         return  new  A ();     } } 
 
1 2 3 4 5 6 public  class  ConfigB  {    @Bean      public  B getB ()  {         return  new  B ();     } } 
 
1 2 3 @Import({ConfigA.class, ConfigB.class}) public  class  ConfigC  {} 
 
1 2 3 4 5 6 7 public  class  Main  {    public  static  void  main (String[] args)  {         ApplicationContext  context  =  new  AnnotationConfigApplicationContext (ConfigC.class);         A  a  =  context.getBean(A.class);         B  b  =  context.getBean(B.class);     } } 
 
除了导入配置类,@Import 注解还有一些更高级的用法,那就是导入实现了 ImportBeanDefinitionRegistrar 接口或者 ImportSelector 接口的类。在这之前,我们需要知道 Spring 容器最终是通过 ConfigurationClassParser 工具类来解析该注解的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 private  void  processImports (ConfigurationClass configClass, SourceClass currentSourceClass,     Collection<SourceClass> importCandidates, 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)) {           Class<?> candidateClass = candidate.loadClass();           ImportSelector  selector  =  BeanUtils.instantiateClass(candidateClass, ImportSelector.class);           ParserStrategyUtils.invokeAwareMethods(               selector, this .environment, this .resourceLoader, this .registry);           if  (selector instanceof  DeferredImportSelector) {             this .deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);           }           else  {                          String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());             Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);             processImports(configClass, currentSourceClass, importSourceClasses, false );           }         }                  else  if  (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {           Class<?> candidateClass = candidate.loadClass();           ImportBeanDefinitionRegistrar  registrar  =                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);           ParserStrategyUtils.invokeAwareMethods(               registrar, this .environment, this .resourceLoader, this .registry);           configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());         }         else  {                      this .importStack.registerImport(               currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());           processConfigurationClass(candidate.asConfigClass(configClass));         }       }     }     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();     }   } } 
 
如果导入的是一个 ImportSelector,那么在这里会调用它的 selectImports 方法获取更多需要导入到容器的类,典型的比如 Spring Boot 的 @EnableAutoConfiguration 自动装配注解。
如果导入的是一个 ImportBeanDefinitionRegistrar,那么会在这里将它添加到配置类的 importBeanDefinitionRegistrars 属性中。Spring 容器在初始化时,会通过 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitions 方法获取所有的 BeanDefinition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public  void  loadBeanDefinitions (Set<ConfigurationClass> configurationModel)  {  TrackedConditionEvaluator  trackedConditionEvaluator  =  new  TrackedConditionEvaluator ();   for  (ConfigurationClass configClass : configurationModel) {     loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);   } } private  void  loadBeanDefinitionsForConfigurationClass (     ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator)  {  if  (trackedConditionEvaluator.shouldSkip(configClass)) {     String  beanName  =  configClass.getBeanName();     if  (StringUtils.hasLength(beanName) && this .registry.containsBeanDefinition(beanName)) {       this .registry.removeBeanDefinition(beanName);     }     this .importRegistry.removeImportingClass(configClass.getMetadata().getClassName());     return ;   }   if  (configClass.isImported()) {     registerBeanDefinitionForImportedConfigurationClass(configClass);   }   for  (BeanMethod beanMethod : configClass.getBeanMethods()) {     loadBeanDefinitionsForBeanMethod(beanMethod);   }   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());      loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); } 
 
很多第三方框架在集成 Spring 的时候,都会通过实现 ImportBeanDefinitionRegistrar 接口将自定义的 BeanDefinition 注册到 Spring 容器中,比如 MyBatis 的 @MapperScan 注解。