Spring-BootSpring boot

一分钟了解@Import注解

2019-04-21  本文已影响0人  chzne

@import代码如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    /**
     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
     * or regular component classes to import.
     */
    Class<?>[] value();

}

@Import注解主要用于导入某些特殊的Bean,这些特殊的Bean和Bean Definitaion 有关。主要用于导入@Configuration 类,ImportSelector和ImportBeanDefinitionRegistrar接口的实现类 ,当然如果你有需要的话普通@component类同样也是可以导入的。

所提供的功能和Spring XML中的<import/>元素是一样的。

XML和其他非Bean definition源需要使用@ImportResource导入

导入@Configuration配置类

在Spring boot 中配置都一般都是自动导入的,所以我们不需要使用@Import,但是如果如果你自动扫包路径为:com.spring.example.app,而你想导入的配置类在com.spring.example.config下 ,那么该配置类就需要使用@Import导入,尤其是第三方jar包的配置类都需要借助@Import来导入

案列演示

目录结构


image.png
@Import(OutConfiguration.class)
@SpringBootApplication
@Log4j2
public class ImportExampleSpringBootApplication {

   public static void main(String[] args) {

       ConfigurableApplicationContext content = SpringApplication.run(ImportExampleSpringBootApplication.class, args);
       Student student = (Student) content.getBean("student");
       log.info("the name of the student is [{}]",student.getName());
   }

}

public class Student {

    protected String name;



    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }




}
@Configuration
public class OutConfiguration {

    @Bean
    public Student student(){

        return new Student("inner");
    }
}

OutConfiguration和ImportExampleSpringBootApplication在同级的不同目录下如果我们需要用到OutConfiguration配置类
那么我们要用 @import导入OutConfiguration类

代码输出:
 [           main] com.spring.example.app.ImportExample     : the name of the student is [inner]

ImportSelector导入配置类

@Component
@Log4j2
public class MyImportSelector implements ImportSelector {
    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     *
     * @param importingClassMetadata
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        importingClassMetadata.getAnnotationTypes().forEach(config->{

            log.info(config);
        });

        return null;

    }
}

如上所示:如果只是用@Component注释ImportSelector的实现类
MyImportSelector 该类的selectImports方法是不会被调用的
我们需要使用@import导入自动配置文件选择器
之前使用的是@Import(OutConfiguration.class)导入配置类
那么现在我们使用MyImportSelector 来导入OutConfiguration.class配置类

@Import({ MyImportSelector.class})
@SpringBootApplication
@Log4j2
public class ImportExampleSpringBootApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext content = SpringApplication.run(ImportExampleSpringBootApplication.class, args);
        Student student = (Student) content.getBean("student");
        log.info("the name of the student is [{}]",student.getName());
    }

}
public class MyImportSelector implements ImportSelector {
    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     *
     * @param importingClassMetadata
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        importingClassMetadata.getAnnotationTypes().forEach(System.out::println);

        return new String[]{OutConfiguration.class.getName()};

    }
}
代码输出:
 [           main] com.spring.example.app.ImportExample     : the name of the student is [inner]

MyImportSelector中我们简单的返回了OutConfiguration所以最终效果和之前的案例效果是完全一样的

ImportSelector代码原文翻译

导入ImportBeanDefinitionRegistrar的实现类

ImportBeanDefinitionRegistrar 代码原文翻译

@Import({ MyImportBeanDefinitionRegistrar.class})
@SpringBootApplication
@Log4j2
public class ImportExampleSpringBootApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext content = SpringApplication.run(ImportExampleSpringBootApplication.class, args);
        Student student = (Student) content.getBean("student");
        log.info("the name of the student is [{}]",student.getName());
    }

}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * Register bean definitions as necessary based on the given annotation metadata of
     * the importing {@code @Configuration} class.
     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
     * registered here, due to lifecycle constraints related to {@code @Configuration}
     * class processing.
     *
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry               current bean definition registry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        BeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClassName(Student.class.getName());
        ConstructorArgumentValues arg = new ConstructorArgumentValues();
        arg.addIndexedArgumentValue(0,"inner");
        ((GenericBeanDefinition) beanDefinition).setConstructorArgumentValues(arg);
        registry.registerBeanDefinition("student",beanDefinition);
    }
}

ImportBeanDefinitionRegistrar 注册器主要是在Spring boot 启动
阶段动态的向容器中注册Bean Definition 像Mybatis中的Mapper
就是通过实现BeanDefinitionRegistrar接口来自动注册到容器中的
代码如下

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {

/**
 * A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of
 * MyBatis mapper scanning. Using an @Enable annotation allows beans to be
 * registered via @Component configuration, whereas implementing
 * {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
 *
 * @author Michael Lanyon
 * @author Eduardo Macarron
 * @author Putthiphong Boonphong
 *
 * @see MapperFactoryBean
 * @see ClassPathMapperScanner
 * @since 1.2.0
 */
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry);
    }
  }

  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader);

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toList()));

    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("basePackages"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toList()));

    basePackages.addAll(
        Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
            .map(ClassUtils::getPackageName)
            .collect(Collectors.toList()));

    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

  /**
   * A {@link MapperScannerRegistrar} for {@link MapperScans}.
   * @since 2.0.0
   */
  static class RepeatingRegistrar extends MapperScannerRegistrar {
    /**
     * {@inheritDoc}
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
        BeanDefinitionRegistry registry) {
      AnnotationAttributes mapperScansAttrs = AnnotationAttributes
          .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
      if (mapperScansAttrs != null) {
        Arrays.stream(mapperScansAttrs.getAnnotationArray("value"))
            .forEach(mapperScanAttrs -> registerBeanDefinitions(mapperScanAttrs, registry));
      }
    }
  }

}
上一篇下一篇

猜你喜欢

热点阅读