美文网首页Spring-BootSpring boot
一分钟了解@Import注解

一分钟了解@Import注解

作者: chzne | 来源:发表于2019-04-21 00:57 被阅读0次

    @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));
          }
        }
      }
    
    }
    

    相关文章

      网友评论

        本文标题:一分钟了解@Import注解

        本文链接:https://www.haomeiwen.com/subject/apargqtx.html