zoukankan      html  css  js  c++  java
  • @Import注解源码

    前言

    平常在使用SpringBoot的过程中,经常会使用到@EnableXXX的注解,而随之一起的还有一个@Import注解,这次就专门来看@Import的源码

    正文

    先摘抄一部分它的英文注释吧:

    /**
     * // 导入一个或多个组件类,代表性的就是Configuration类型的类
     * Indicates one or more <em>component classes</em> to import &mdash; typically
     * {@link Configuration @Configuration} classes.
     *
     * // 提供与spring的xml中的import标签相同的功能。可以导入配置类,普通类,ImportSelector或ImportBeanDefinitionRegistrar接口的实现类
     * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
     * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
     * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
     * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
     *
     * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
     * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
     * injection. Either the bean itself can be autowired, or the configuration class instance
     * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
     * navigation between {@code @Configuration} class methods.
     *
     * <p>May be declared at the class level or as a meta-annotation.
     *
     * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
     * imported, use the {@link ImportResource @ImportResource} annotation instead.
     *
     */
    
    

    大概意思就是:

    1. 可以导入一个类或多个类至容器中,和xml中的import标签功能一样
    2. 可导入普通类,Configuration类,ImportSelector或ImportBeanDefinitionRegistrar接口的实现类
    3. 导入配置文件,该配置文件可被其他的bean注入,也可以在配置中注入其他的bean。

    例子

    1. 新建一个springboot项目
    2. 在启动类上层建立同层级的包。例如,启动类test.javaa.b.c.d下面,那么在c包下建立e包,ed处于同一级目录。

    为什么要有第二点,因为springboot会扫描启动类同级目录下的文件,如果在同一级目录,直接使用@Componment就行了。所以个人觉得@Import就是导入与项目不是同一级目录的类。

    导入普通类

    在e包下新建两个类,TestImportA和TestImportB:

    
        public class TestImportA {
            public void test(){
                System.out.println("TestImportB run");
            }
        }
    
        public class TestImportB {
            public void test(){
                System.out.println("TestImportB run");
            }
        }
    
    

    然后在启动类上面添加:

    
        @SpringBootApplication
        @Import({TestImportA.class, TestImportB.class})
        public class SpringbootOneApplication {
            public static void main(String[] args) {
                ConfigurableApplicationContext run = SpringApplication.run(SpringbootOneApplication.class, args);
                TestImportB testImportB = run.getBean(TestImportB.class);
                testImportB.test();
            }
        }
    
    

    这样,不会被扫描到的TestImportA.class, TestImportB.class就可以通过@Import注解扫描到,并放进容器了。

    导入配置类

    在e包下新建配置类TestImportConfig

    
        @Configuration
        public class TestImportConfig {
            @Bean
            public TestImportA testImportA() {
                return new TestImportA();
            }
        
            @Bean
            public TestImportB testImportB() {
                return new TestImportB();
            }
        }
    
    

    然后在启动类上面添加:

    
        @SpringBootApplication
        @Import(TestImportConfig.class)
        public class SpringbootOneApplication {
            public static void main(String[] args) {
                ConfigurableApplicationContext run = SpringApplication.run(SpringbootOneApplication.class, args);
                TestImportB testImportB = run.getBean(TestImportB.class);
                testImportB.test();
            }
        }
    
    

    效果和第一种是一样的。

    导入ImportSelector的实现类

    1. 新建一个注解类TestImport
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(ImportSelectorImpl.class) // 注意建立ImportSelectorImpl类后加上这部分注解
    public @interface TestImport {
        String value() default "value";
        boolean flag() default false;
    }
    
    

    说一下为什么要新建一个注解类。
    ImportSelector的接口String[] selectImports(AnnotationMetadata importingClassMetadata),该接口接收了一个AnnotationMetadata,
    这个类里面就包含了@Import注解所在的地方(类,接口,枚举)的注解信息,这样我们就可以在启动配置的时候获取一些需要的信息了。最典型的就是@EnableXXX注解,
    在@EnableXXX中使用@Import导入ImportSelector实现类,这样就可以获取@EnableXXX注解的一些信息了。

    1. 新建一个ImportSelectorImpl来实现ImportSelector接口
    
        public class ImportSelectorImpl implements ImportSelector {
            @Override
            public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                // 获取注解集合
                MergedAnnotations annotations = importingClassMetadata.getAnnotations();
                // 获取TestImport注解信息
                MergedAnnotation<TestImport> testImportMergedAnnotation = annotations.get(TestImport.class);
                // 打印
                System.out.println("===="+testImportMergedAnnotation.getString("value"));
                return new String[]{"a.b.c.e.TestImportA","a.b.c.e.TestImportB"};
            }
        }
    
    
    1. 在启动类上改用@TestImport注解
    
        @SpringBootApplication
        @TestImport
        public class SpringbootOneApplication {
            public static void main(String[] args) {
                ConfigurableApplicationContext run = SpringApplication.run(SpringbootOneApplication.class, args);
                TestImportB testImportB = run.getBean(TestImportB.class);
                testImportB.test();
            }
        }
    
    

    导入ImportBeanDefinitionRegistrar的实现类

    这个用法和ImportSelector的用法是一样的,这里就不全展示了,直接在TestImport注解上换成@Import(ImportBeanDefinitionRegistrarImpl.class)就行了。

    1. 新建ImportBeanDefinitionRegistrar实现类
    
        public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
            @Override
            public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                // 获取注解集合
                MergedAnnotations annotations = importingClassMetadata.getAnnotations();
                // 获取TestImport注解信息
                MergedAnnotation<TestImport> testImportMergedAnnotation = annotations.get(TestImport.class);
                // 打印value属性值
                System.out.println("===="+testImportMergedAnnotation.getString("value"));
                // 注册信息
                registry.registerBeanDefinition("testImportB",new RootBeanDefinition(TestImportB.class));
            }
        }
    
    

    最后

    还记得最近的面试中,被问到了如何向spring容器中注册bean,当时我的回答是:

    1. @Component,@Service,@Controller,@Repository这四个注解,后三个底层都是用的@Component。
    2. @Configuration,在配置类中使用@Bean注册
    3. BeanFactoryAware, ApplicationContextAware,实现这两个接口,然后手动向容器中注册。

    这次就可以另外加上一个@Import,ImportBeanDefinitionRegistrar以及ImportSelector了。

  • 相关阅读:
    (转)CortexM3 (NXP LPC1788)之启动代码分析
    (转)CortexM3 (NXP LPC1788)之RTC
    (转)CortexM3 (NXP LPC1788)之外部中断操作
    (原创)TCP/IP学习笔记之ARP(地址解析协议)
    (原创)CheckTool:CRC校验、累加和校验、异或和校验专业校验工具V1.0
    (转)CortexM3 (NXP LPC1788)之SysTick系统节拍定时器
    (转)CortexM3 (NXP LPC1788)之UART用法
    工具 MyEclipse8.5注册码
    Delphi – UtWinHttp_API.pas From winhttp.h
    Delphi – TCanvas.CopyRect方法中参数CopyMode的意义
  • 原文地址:https://www.cnblogs.com/guoyuchuan/p/15085940.html
Copyright © 2011-2022 走看看