Maison > Java > javaDidacticiel > le corps du texte

Comment utiliser l'annotation @Import dans SpringBoot

WBOY
Libérer: 2023-05-31 18:25:45
avant
1170 Les gens l'ont consulté

    1. @Import introduit les classes ordinaires

    En utilisant l'annotation @Import, nous pouvons définir les classes ordinaires comme Bean . @Import peut être ajouté aux classes correspondant à @SpringBootApplication (classe de démarrage), @Configuration (classe de configuration) et @Component (classe de composant).

    Remarque : @RestController, @Service, @Repository appartiennent tous à @Component

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

    II. @Import introduit une classe de configuration (classe modifiée par @Configuration)

    @Import En plus de définir des classes ordinaires comme Beans, @Import peut également introduire une classe modifiée par @Configuration (introduisant une classe de configuration), Laissez ainsi la classe de configuration prendre effet (tous les beans de la classe de configuration sont ajoutés au conteneur IOC). Il est souvent utilisé lors de la personnalisation des démarreurs.

    Si la classe de configuration se trouve sous la structure standard du package SpringBoot (dans le répertoire racine du package de classe de démarrage SpringBootApplication). Spring Boot gérera automatiquement l'importation des classes de configuration et il n'est pas nécessaire d'utiliser manuellement l'annotation @Import. En règle générale, cette situation s'applique lorsque la classe de configuration @Configuration se trouve en dehors de la structure standard du package Spring Boot. Donc on l'utilise généralement lors de la personnalisation du démarreur.

    @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的使用
             */
        }
    }
    Copier après la connexion

    3. @Import présente la classe d'implémentation de ImportSelector

    @Import peut également introduire la classe d'implémentation de ImportSelector et mettre la classe renvoyée par selectImports( ) de l'interface ImportSelector. Les noms sont définis sous forme de beans. Faites attention au paramètre AnnotationMetadata de la méthode selectImports(). Grâce à ce paramètre, nous pouvons obtenir diverses informations sur la classe annotée avec @Import. Ceci est particulièrement utile pour passer des paramètres. La configuration automatique de SpringBoot et les annotations @EnableXXX existent séparément.

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

    Concernant l'utilisation de la classe d'implémentation de ImportSelector introduite par @Import, donnons quelques scénarios d'utilisation simples (le développement réel est nettement plus compliqué que cela).

    3.1 Scénario d'import statique (injection de classes connues)

    Scénario statique (injection de classes connues), il est très simple de définir ce dont nous avons besoin en tant que beans en implémentant la classe ImportSelector Juste renvoie la classe directement, comme dans l'exemple suivant. Ajoutons une annotation EnableXXX et injectons une classe XXX connue via 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);
        }
    
    }
    Copier après la connexion

    3.2 Scénario d'importation dynamique (injection de classes avec des conditions spécifiées)

    Pour créer une telle fonction, nous devons ajouter toutes les classes qui implémentent l'interface HelloService sous le chemin de package spécifié Ajoutez le bean au conteneur IOC. L'annotation @ComponentScan est utilisée pour nous aider à spécifier le chemin. L'implémentation spécifique est la suivante :

    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 {
    }
    Copier après la connexion

    4. @Import introduit la classe d'implémentation de ImportBeanDefinitionRegistrar

    @Import introduit la classe d'implémentation de ImportBeanDefinitionRegistrar. Généralement utilisé pour enregistrer dynamiquement des beans. Le plus important est que vous puissiez également apporter des modifications ou des améliorations supplémentaires à ces BeanDefinitions. Le mybatis @MapperScan que nous utilisons souvent est implémenté de cette manière.

    /**
     * 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);
    
    }
    Copier après la connexion

    Concernant l'utilisation de ImportBeanDefinitionRegistrar introduit par @Import, il est fortement recommandé de jeter un œil au code source de traitement de mybatis à propos de @MapperScan. Très intéressant. Nous donnons également un exemple très simple pour permettre à tout le monde de voir intuitivement l'utilisation de ImportBeanDefinitionRegistrar. Par exemple, nous souhaitons enregistrer toutes les classes avec des annotations BeanIoc dans le chemin de package spécifié en tant que beans.

    La mise en œuvre spécifique est la suivante :

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

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Étiquettes associées:
    source:yisu.com
    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
    Tutoriels populaires
    Plus>
    Derniers téléchargements
    Plus>
    effets Web
    Code source du site Web
    Matériel du site Web
    Modèle frontal