Heim  >  Artikel  >  Java  >  So verwenden Sie die @Import-Annotation in SpringBoot

So verwenden Sie die @Import-Annotation in SpringBoot

WBOY
WBOYnach vorne
2023-05-31 18:25:451167Durchsuche

    1. @Import führt gewöhnliche Klassen ein

    Mithilfe der @Import-Annotation können wir gewöhnliche Klassen als Bean definieren. @Import kann zu den Klassen hinzugefügt werden, die @SpringBootApplication (Startklasse), @Configuration (Konfigurationsklasse) und @Component (Komponentenklasse) entsprechen.

    Hinweis: @RestController, @Service, @Repository gehören alle zu @Component

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

    II. @Import führt eine Konfigurationsklasse ein (von @Configuration geänderte Klasse)

    @Import Zusätzlich zur Definition gewöhnlicher Klassen als Beans kann @Import auch eine von @Configuration geänderte Klasse einführen (Einführung einer Konfigurationsklasse). dadurch Lassen Sie die Konfigurationsklasse wirksam werden (alle Beans unter der Konfigurationsklasse werden dem IOC-Container hinzugefügt). Es wird häufig beim Anpassen von Startern verwendet.

    Wenn sich die Konfigurationsklasse unter der Standard-SpringBoot-Paketstruktur befindet (im Stammverzeichnis des SpringBootApplication-Startklassenpakets). Spring Boot übernimmt automatisch den Import von Konfigurationsklassen und es ist nicht erforderlich, die Annotation @Import manuell zu verwenden. Typischerweise tritt diese Situation auf, wenn die @Configuration-Konfigurationsklasse außerhalb der Standard-Spring-Boot-Paketstruktur liegt. Daher wird es im Allgemeinen beim Anpassen des Anlassers verwendet.

    @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 führt die Implementierungsklasse von ImportSelector ein.

    @Import kann auch die Implementierungsklasse von ImportSelector einführen und die von selectImports zurückgegebene Klasse verwenden ()-Methode der ImportSelector-Schnittstelle Namen werden als Beans definiert. Beachten Sie den Parameter AnnotationMetadata der Methode selectImports(). Über diesen Parameter können wir verschiedene Informationen über die mit @Import annotierte Klasse erhalten. Dies ist besonders nützlich für die Übergabe von Parametern. Die automatische Konfiguration von SpringBoot und die @EnableXXX-Anmerkungen sind separat vorhanden.

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

    Bezüglich der Verwendung der von @Import eingeführten Implementierungsklasse von ImportSelector geben wir einige einfache Verwendungsszenarien (die tatsächliche Entwicklung ist definitiv komplizierter).

    3.1 Statisches Importszenario (Injektion bekannter Klassen)

    Statisches Szenario (Injektion bekannter Klassen). Es ist sehr einfach, zu definieren, was wir als Beans benötigen, indem wir die ImportSelector-Klasse Just implementieren Geben Sie die Klasse direkt zurück, wie im folgenden Beispiel. Fügen wir eine EnableXXX-Annotation hinzu und fügen Sie über XXXConfigurationSelector eine bekannte Klasse XXX ein.

    /**
     * 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 Dynamisches Importszenario (Injizieren von Klassen mit angegebenen Bedingungen)

    Um eine solche Funktion zu erstellen, müssen wir alle Klassen, die die HelloService-Schnittstelle implementieren, unter dem angegebenen Paketpfad hinzufügen Fügen Sie die Bean dem IOC-Container hinzu. Die Annotation @ComponentScan wird verwendet, um uns bei der Angabe des Pfads zu helfen. Die spezifische Implementierung lautet wie folgt:

    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 führt die Implementierungsklasse von ImportBeanDefinitionRegistrar ein

    @Import führt die Implementierungsklasse von ImportBeanDefinitionRegistrar ein. Wird im Allgemeinen zur dynamischen Registrierung von Beans verwendet. Das Wichtigste ist, dass Sie an diesen BeanDefinitions auch zusätzliche Modifikationen oder Erweiterungen vornehmen können. Der von uns häufig verwendete mybatis @MapperScan wird auf diese Weise implementiert.

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

    Bezüglich der Verwendung von ImportBeanDefinitionRegistrar, eingeführt von @Import, wird dringend empfohlen, einen Blick auf den Verarbeitungsquellcode von mybatis über @MapperScan zu werfen. Sehr interessant. Wir geben auch ein sehr einfaches Beispiel, damit jeder die Verwendung von ImportBeanDefinitionRegistrar intuitiv sehen kann. Beispielsweise möchten wir alle Klassen mit BeanIoc-Annotationen im angegebenen Paketpfad als Beans registrieren.

    Die spezifische Implementierung ist wie folgt:

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

    Das obige ist der detaillierte Inhalt vonSo verwenden Sie die @Import-Annotation in SpringBoot. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen