Spring中@Import注解的使用及原理分析

经常面试的时候会被问到了解SpringBoot的自动配置原理吗?大部分都是向下面这边解释其实并不了解其中的原理

@SpringBootApplication ---> @EnableAutoConfiguration --> @Import({AutoConfigurationImportSelector.class})

但是如果面试官进一步的追问 知道什么时候处理 @Import 吗 ? 或者AutoConfigurationImportSelector 的selectImports方法是什么时机被执行的吗?

估计就凉凉了

  下面来认识一下@Import

   @Import是在org.springframework.context.annotation包中的

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}

Spring中@Import注解一般是和 ImportSelector 和 ImportBeanDefinitionRegistrar 这两个接口配合使用的,后面会解释原因

当然也是可以单独使用

1 简单定义几个Bean

public class ImportBean1 {
}

public class ImportBean2 {
}

public class ImportBean3 {
}

2 定义配置类

public class MyImportSelector implements ImportSelector {
   @Override
   public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      return new String[]{"com.xxx.service.ImportBean2"};
   }
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(ImportUserService.class);

      registry.registerBeanDefinition("importUserService",rootBeanDefinition);
   }
}

3 定义启动配置类

@Import({ImportBean1.class,MyImportBeanDefinitionRegistrar.class, MyImportSelector.class})
public class AppConfig {
}

4 定义启动类和验证

public class Test {
   public static void main(String[] args) {

      AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
      ImportBean3 importBean3 = applicationContext.getBean("importBean3",ImportBean3.class);
      System.out.println(importBean3);
      
      ImportBean1 importBean1 = applicationContext.getBean( ImportBean1.class);
      System.out.println(importBean1);
      
      ImportBean2 importBean2 = applicationContext.getBean( ImportBean2.class);
      System.out.println(importBean2);

       }

}

经过验证都是可以获取到ImportBean1/ImportBean2/importBean3

原理解释:

其实@Import能够生效,主要依靠于Spring一个非常重要的BeanFactoryPostProcessor的实现 ConfigurationClassPostProcessor的后置

处理器的核心方法org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   int registryId = System.identityHashCode(registry);
   if (this.registriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
   }
   if (this.factoriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
   }
   this.registriesPostProcessed.add(registryId);

   // 寻找配置类对应的BeanDefinition然后进行处理
   // 比如开始扫描
   processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   String[] candidateNames = registry.getBeanDefinitionNames();
   .........
   do {
      // 对配置BeanDefinition进行解析,解析完后会生成ConfigurationClass
      parser.parse(candidates);   //AppConfig.class
      parser.validate();

     ..........
   }
   while (!candidates.isEmpty());
上面省略的一些调用栈,应该很容易跟
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {

   // 1. 如果配置bean上有@Component注解,递归去解析内部类上的注解
   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass);
   }

   // Process any @PropertySource annotations
   // 2. 解析@PropertySource注解
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations
   // 3. 解析@ComponentScan注解,并进行扫描
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {

         // The config class is annotated with @ComponentScan -> perform the scan immediately
         // 扫描得到BeanDefinition
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }

            // 检查扫描所得到BeanDefinition是不是配置Bean,基本上都有@Component注解,所以都是配置类
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations
   // 4. 解析@Import,getImports方法返回AppConfig.class上定义的Import注解中所导入的类的信息
   processImports(configClass, sourceClass, getImports(sourceClass), true);

   // Process any @ImportResource annotations
   // 5. 解析@ImportResource,得到导入进来的spring的xml配置文件,然后解析
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);

         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   // Process individual @Bean methods
   // 6. 解析配置类中的加了@Bean注解的方法
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   // 7. 如果配置类实现了某个接口,那么则解析该接口中的加了@Bean注解的默认方法
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   // 8. 如果有父类,则返回父类给上层遍历进行处理
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

 

上面代码中的第4步就是处理AppConfig上的@Import的,里面的代码相信你稍微跟一些 就应该能够明白,我比较懒所以就不多说了 

参考:https://blog.csdn.net/baidu_37107022/article/details/88962331

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值