Rumah  >  Artikel  >  Java  >  Cara menggunakan anotasi @Import dalam SpringBoot

Cara menggunakan anotasi @Import dalam SpringBoot

WBOY
WBOYke hadapan
2023-05-31 18:25:451164semak imbas

    1 @Import memperkenalkan kelas biasa

    Dengan menggunakan anotasi @Import, kami boleh mentakrifkan kelas biasa sebagai Beans. @Import boleh ditambah pada kelas yang sepadan dengan @SpringBootApplication (kelas permulaan), @Configuration (kelas konfigurasi) dan @Component (kelas komponen).

    Nota: @RestController, @Service, dan @Repository semuanya milik @Component

    @SpringBootApplication
    @Import(ImportBean.class) // 通过@Import注解把ImportBean添加到IOC容器里面去
    public class MyBatisApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyBatisApplication.class, args);
        }
    }

    2. @Import memperkenalkan kelas konfigurasi ( @Kelas pengubahsuaian konfigurasi)

    @Import Selain mentakrifkan kelas biasa sebagai Bean, @Import juga boleh memperkenalkan kelas diubahsuai @Configuration (memperkenalkan kelas konfigurasi), dengan itu menjadikan kelas konfigurasi berkuat kuasa (semua objek di bawah kelas konfigurasi Bean ditambah pada bekas IOC). Ia sering digunakan apabila menyesuaikan pemula.

    Jika kelas konfigurasi berada di bawah struktur pakej SpringBoot standard (dalam direktori akar pakej kelas permulaan SpringBootApplication). Spring Boot akan mengendalikan import kelas konfigurasi secara automatik dan tidak perlu menggunakan anotasi @Import secara manual. Biasanya, keadaan ini terpakai apabila kelas konfigurasi @Configuration berada di luar struktur pakej Spring Boot standard. Jadi ia biasanya digunakan apabila menyesuaikan pemula.

    @Configuration(proxyBeanMethods = false)
    @Import({  // import了两个哈
            XXXDataConfiguration.XXXPartOneConfiguration.class,
            XXXDataConfiguration.XXXPartTwoConfiguration.class
    })
    public class XXXDataAutoConfiguration {
    }
    public class XXXDataConfiguration {
        @Configuration(proxyBeanMethods = false)
        static class XXXPartOneConfiguration {
            @Bean
            @ConditionalOnMissingBean
            public BeanForIoc beanForIoc() {
                return new BeanForIoc();
            }
        }
        @Configuration(proxyBeanMethods = false)
        static class XXXPartTwoConfiguration {
    
            /**
             * 省略了@Bean的使用
             */
        }
    }

    3 @Import memperkenalkan kelas pelaksanaan ImportSelector

    @Import juga boleh memperkenalkan kelas pelaksanaan ImportSelector dan mentakrifkan nama Kelas yang dikembalikan oleh kaedah selectImports() Antara muka ImportSelector sebagai kacang . Beri perhatian kepada parameter AnnotationMetadata kaedah selectImports() Melalui parameter ini, kita boleh mendapatkan pelbagai maklumat tentang Kelas yang dianotasi dengan @Import. Ini amat berguna untuk menghantar parameter. Konfigurasi automatik SpringBoot dan anotasi @EnableXXX wujud secara berasingan.

    public interface ImportSelector {
        /**
         * 用于指定需要注册为bean的Class名称
         * 当在@Configuration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把实现类中返回的Class名称都定义为bean。
         *
         * 通过其参数AnnotationMetadata importingClassMetadata可以获取到@Import标注的Class的各种信息,
         * 包括其Class名称,实现的接口名称、父类名称、添加的其它注解等信息,通过这些额外的信息可以辅助我们选择需要定义为Spring bean的Class名称
         */
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    }

    Berkenaan penggunaan kelas pelaksanaan ImportSelector yang diperkenalkan oleh @Import, mari berikan beberapa senario penggunaan mudah (pembangunan sebenar pastinya lebih rumit daripada ini).

    3.1 Senario import statik (menyuntik kelas yang diketahui)

    Senario statik (menyuntik kelas yang diketahui), sangat mudah untuk mengembalikan kelas secara langsung yang perlu kita takrifkan sebagai kacang dengan melaksanakan kelas ImportSelector Baiklah, seperti contoh berikut. Mari tambahkan anotasi EnableXXX dan masukkan kelas XXX yang diketahui melalui XXXConfigurationSelector.

    /**
     * XXXConfigurationSelector一定要配合@Import使用
     */
    public class XXXConfigurationSelector implements ImportSelector {
        @Override
        @NonNull
        public String[] selectImports(@NonNull AnnotationMetadata importingClassMetadata) {
            // 把XXX对应的类,定义为Bean
            return new String[]{XXX.class.getName()};
        }
    }
    /**
     * 注意 @Import(XXXConfigurationSelector.class)
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(XXXConfigurationSelector.class)
    public @interface EnableXXX {
    }
    @SpringBootApplication
    @EnableXXX // 使之生效
    public class MyBatisApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MyBatisApplication.class, args);
        }
    
    }

    3.2 Senario import dinamik (menyuntik kelas dengan keadaan tertentu)

    Untuk membuat fungsi sedemikian, kita perlu menambah semua kelas yang melaksanakan antara muka HelloService di bawah laluan pakej yang ditentukan sebagai beans to Go di dalam bekas IOC. Anotasi @ComponentScan digunakan untuk membantu kami menentukan laluan. Pelaksanaan khusus adalah seperti berikut:

    public interface HelloService {
        void function();
    
    }
    public class DynamicSelectImport implements ImportSelector {
        /**
         * DynamicSelectImport需要配合@Import()注解使用
         * <p>
         * 通过其参数AnnotationMetadata importingClassMetadata可以获取到@Import标注的Class的各种信息,
         * 包括其Class名称,实现的接口名称、父类名称、添加的其它注解等信息,通过这些额外的信息可以辅助我们选择需要定义为Spring bean的Class名称
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // 第一步:获取到通过ComponentScan指定的包路径
            String[] basePackages = null;
            // @Import注解对应的类上的ComponentScan注解
            if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
                Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
                basePackages = (String[]) annotationAttributes.get("basePackages");
            }
            if (basePackages == null || basePackages.length == 0) {
                //ComponentScan的basePackages默认为空数组
                String basePackage = null;
                try {
                    // @Import注解对应的类的包名
                    basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                basePackages = new String[]{basePackage};
            }
            // 第er步,知道指定包路径下所有实现了HelloService接口的类(ClassPathScanningCandidateComponentProvider的使用)
            ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
            TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);
            scanner.addIncludeFilter(helloServiceFilter);
            Set<String> classes = new HashSet<>();
            for (String basePackage : basePackages) {
                scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
            }
            // 第三步,返回添加到IOC容器里面去
            return classes.toArray(new String[0]);
        }
    }
    @Configuration
    @ComponentScan("com.tuacy.collect.mybatis") // 指定路径
    @Import(DynamicSelectImport.class)
    public class DynamicSelectConfig {
    }

    4. @Import memperkenalkan kelas pelaksanaan ImportBeanDefinitionRegistrar

    @Import memperkenalkan kelas pelaksanaan ImportBeanDefinitionRegistrar. Biasanya digunakan untuk mendaftarkan kacang secara dinamik. Perkara yang paling penting ialah anda boleh membuat pengubahsuaian atau peningkatan tambahan pada BeanDefinitions ini. Mybatis @MapperScan yang sering kami gunakan dilaksanakan dengan cara ini.

    /**
     * ImportBeanDefinitionRegistrar,我们一般会实现ImportBeanDefinitionRegistrar类,然后配合一个自定义的注解一起使用。而且在注解类上@Import我们的这个实现类。
     * 通过自定义注解的配置,拿到注解的一些元数据。然后在ImportBeanDefinitionRegistrar的实现类里面做相应的逻辑处理,比如把自定义注解标记的类添加到Spring IOC容器里面去。
     */
    public interface ImportBeanDefinitionRegistrar {
    
        /**
         * 根据注解的给定注释元数据,根据需要注册bean定义
         * @param importingClassMetadata 可以拿到@Import的这个class的Annotation Metadata
         * @param registry BeanDefinitionRegistry 就可以拿到目前所有注册的BeanDefinition,然后可以对这些BeanDefinition进行额外的修改或增强。
         */
        void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
    
    }

    Berkenaan penggunaan ImportBeanDefinitionRegistrar yang diperkenalkan oleh @Import, amat disyorkan agar anda melihat kod sumber pemprosesan mybatis untuk @MapperScan. Sangat menarik. Kami juga memberikan contoh yang sangat mudah untuk membolehkan semua orang melihat penggunaan ImportBeanDefinitionRegistrar secara intuitif Sebagai contoh, kami ingin mendaftarkan semua kelas dengan anotasi BeanIoc dalam laluan pakej yang ditentukan sebagai kacang.

    Pelaksanaan khusus adalah seperti berikut:

    /**
     * 我们会把添加了该注解的类作为bean
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface BeanIoc {
    
    }
    
    /**
     * 定义包路径。(指定包下所有添加了BeanIoc注解的类作为bean)
     * 注意这里 @Import(BeanIocScannerRegister.class) 的使用
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(BeanIocScannerRegister.class)
    public @interface BeanIocScan {
        String[] basePackages() default "";
    }
    
    
    /**
     * 搜索指定包下所有添加了BeanIoc注解的类,并且把这些类添加到ioc容器里面去
     */
    public class BeanIocScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    
        private final static String PACKAGE_NAME_KEY = "basePackages";
    
        private ResourceLoader resourceLoader;
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            //1. 从BeanIocScan注解获取到我们要搜索的包路径
            AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(BeanIocScan.class.getName()));
            if (annoAttrs == null || annoAttrs.isEmpty()) {
                return;
            }
            String[] basePackages = (String[]) annoAttrs.get(PACKAGE_NAME_KEY);
            // 2. 找到指定包路径下所有添加了BeanIoc注解的类,并且把这些类添加到IOC容器里面去
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, false);
            scanner.setResourceLoader(resourceLoader);
            scanner.addIncludeFilter(new AnnotationTypeFilter(BeanIoc.class));
            scanner.scan(basePackages);
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    }
    
    
    /**
     * 使用,使BeanIocScan生效
     */
    @Configuration
    @BeanIocScan(basePackages = "com.tuacy.collect.mybatis")
    public class BeanIocScanConfig {
    }

    Atas ialah kandungan terperinci Cara menggunakan anotasi @Import dalam SpringBoot. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam