ホームページ  >  記事  >  Java  >  SpringBoot で @Import アノテーションを使用する方法

SpringBoot で @Import アノテーションを使用する方法

WBOY
WBOY転載
2023-05-31 18:25:451164ブラウズ

    1. @Import による通常クラスの導入

    @Import アノテーションを使用すると、通常のクラスを Bean として定義できます。 @Importは、@SpringBootApplication(スタートアップクラス)、@Configuration(コンフィグレーションクラス)、@Component(コンポーネントクラス)に対応するクラスに追加できます。

    注: @RestController、@Service、@Repository はすべて @Component

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

    に属します。 2. @Import は構成クラス (@) を導入します。構成変更クラス)

    @Import @Importは通常のクラスをBeanとして定義するだけでなく、@Configurationで変更したクラスを導入(構成クラスの導入)することで、構成クラス(配下のすべてのオブジェクト)を有効にすることもできます。構成クラス Bean が IOC コンテナに追加されます)。スターターをカスタマイズするときによく使用されます。

    構成クラスが標準の SpringBoot パッケージ構造 (SpringBootApplication スタートアップ クラス パッケージのルート ディレクトリ) にある場合。 Spring Boot は構成クラスのインポートを自動的に処理するため、 @Import アノテーションを手動で使用する必要はありません。通常、この状況は、@Configuration 構成クラスが標準の Spring Boot パッケージ構造の外側にある場合に当てはまります。そのため、スターターをカスタマイズするときに一般的に使用されます。

    @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 は、ImportSelector の実装クラスを導入します。

    @Import は、ImportSelector の実装クラスを導入し、selectImports() メソッドによって返されるクラス名を定義することもできます。 ImportSelector インターフェースを Bean として使用します。 selectImports() メソッドの AnnotationMetadata パラメータに注目すると、このパラメータを通じて @Import アノテーションが付けられたクラスに関するさまざまな情報を取得できます。これはパラメータを渡す場合に特に便利です。 SpringBoot の自動設定と @EnableXXX アノテーションは別に存在します。

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

    @Import によって導入された ImportSelector の実装クラスの使用法について、簡単な使用シナリオをいくつか示します (実際の開発は間違いなくこれより複雑です)。

    3.1 静的インポート シナリオ (既知のクラスの挿入)

    静的シナリオ (既知のクラスの挿入)。ImportSelector クラスを実装することで、Bean として定義する必要があるクラスを直接返すのは非常に簡単です。さて、次の例のように。 EnableXXX アノテーションを追加し、XXXConfigurationSelector を通じて既知のクラス XXX を注入しましょう。

    /**
     * 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 動的インポート シナリオ (指定された条件でクラスを挿入する)

    このような関数を作成するには、指定されたパッケージ パスの下にある HelloService インターフェイスを実装するすべてのクラスを Go に Bean として追加する必要があります。 IOCコンテナ内。 @ComponentScan アノテーションは、パスの指定に役立ちます。具体的な実装は次のとおりです。

    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 は、ImportBeanDefinitionRegistrar の実装クラスを導入します。

    @Import は、ImportBeanDefinitionRegistrar の実装クラスを導入します。通常、Bean を動的に登録するために使用されます。最も重要な点は、これらの BeanDefinition に対して 追加の変更や機能拡張を行うこともできるということです。私たちがよく使う mybatis @MapperScan はこの方法で実装されています。

    /**
     * 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);
    
    }

    @Import によって導入された ImportBeanDefinitionRegistrar の使用については、mybatis の @MapperScan の処理ソース コードを参照することを強くお勧めします。とても興味深い。また、誰もが ImportBeanDefinitionRegistrar の使用法を直感的に理解できるように、非常に簡単な例も示しています。たとえば、指定されたパッケージ パス内の BeanIoc アノテーションを持つすべてのクラスを Bean として登録したいとします。

    具体的な実装は次のとおりです:

    /**
     * 我们会把添加了该注解的类作为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 {
    }

    以上がSpringBoot で @Import アノテーションを使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。