• @Import注解的应用和扩展


    将一个对象交给Spring来管理,有三种做法:
    1、@Bean
    2、@Componet(@Service等归为一类)
    3、@Import
    这里主要讲第三种做法,打开Spring源码

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
        Class<?>[] value();
    }
    

    @Import注解只有一个value方法,注释中指明该注解必须作用于@Configuration定义的类上,value可以为想要交给Spring管理的类文件数组、ImportSelector或ImportBeanDefinitionRegistrar,接下来我们依次执行三种做法

    • 1、指定class数组
      首先定义两个类
    public class Apple {
    }
    public class Banana {
    }
    

    然后定义配置类,并用@Import注解装饰,输入两个自定义类

    import com.lwl.entity.Apple;
    import com.lwl.entity.Banana;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration
    @Import({Apple.class, Banana.class})
    public class AppConfig {
    }
    

    测试类中打印容器中类的名称

    public class Test {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
            for (String s : applicationContext.getBeanDefinitionNames()) {
                System.out.println(s);
            }
        }
    }
    

    输出结果中可以看到Apple和Banana都被成功注入:

    img

    结果1

    • 2、实现ImportSelector接口
      定义一个新的实体,需求是通过ImportSelector将其注入Spring容器
    public class Berry {
    }
    

    自定义selector实现ImportSelector接口,在方法中返回自定义的类路径,Spring会自动将该路径下的类注入到容器中

    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class BerryImportSelector implements ImportSelector {
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.lwl.entity.Berry"};
        }
    }
    

    修改配置代码,在@Import中加入BerryImportSelector :

    @Configuration
    @Import({Apple.class, Banana.class, BerryImportSelector.class})
    public class AppConfig {
    }
    

    测试代码不变,打印结果:

    img

    结果2

    Berry确实被注入进来了

    • 3、实现ImportBeanDefinitionRegistrar接口
      再定义一个新的实体:
    public class Tomato {
    }
    

    创建TomatoRegistrar实现ImportBeanDefinitionRegistrar接口,在方法当中将类注册到容器里,并将beanName修改为MyTomato:

    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class TomatoRegistrar implements ImportBeanDefinitionRegistrar {
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Tomato.class);
            beanDefinitionRegistry.registerBeanDefinition("MyTomato", beanDefinition);
        }
    }
    

    修改AppConfig代码,将TomatoRegistrar放入@Import中:

    @Configuration
    @Import({Apple.class, Banana.class, BerryImportSelector.class, TomatoRegistrar.class})
    public class AppConfig {
    }
    

    测试结果:

    img

    结果3

    总结

    在平时的业务开发当中,将对象放入容器,使用@Bean和@Compont基本就能够满足需求,但是@Import注解能够方便扩展功能,举例:

    • 1、控制类注入时机
      我希望能够通过一个简单的开关来控制是否注入Berry类,我们可以定义一个注解
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Import({BerryImportSelector.class})
    public @interface EnableBerry {
    }
    

    修改@AppConfig,删除@Import中的BerryImportSelector.class,

    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration
    @Import({Apple.class, Banana.class, TomatoRegistrar.class})
    public class AppConfig {
    }
    

    这时候运行测试,发现Berry没有被注入到容器中:

    img

    image.png

    如果在AppConfig类加上@EnableBerry注解

    @Configuration
    @Import({Apple.class, Banana.class, TomatoRegistrar.class})
    @EnableBerry
    public class AppConfig {
    }
    

    再次执行测试,Berry成功注入:

    img

    image.png

    SpringCloud中的@EnableEureka、@EnableDiscoveryClient就是利用这个原理

    • 2、通过代理来改变bean定义
      Spring-Mybatis的@MapperScan注解,是由@Import注解所修饰,并注入了MapperScannerRegistrar类:
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import({MapperScannerRegistrar.class})
    public @interface MapperScan {
    

    它在registerBeanDefinitions方法中扫描了基础包,

    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     scanner.doScan(StringUtils.toStringArray(basePackages));
    }
    

    然后提取mapper产生代理类,最后注册到容器当中

    本文示例代码见LiuWillow的github——demo-import

    spring源码

    本文转自https://www.jianshu.com/p/e6b44d8cec5a

  • 相关阅读:
    Windows常用快捷键
    ArrayList和LinkedList的区别
    ICMP TYPE-CODE查阅表
    dedecmsV5.7 百度编辑器ueditor 多图上传 在线管理 排序问题
    dedecmsV5.7 后台上传m4a的音频之后不展示
    php5.6 上传图片error代码为6 或者 报错“PHP Warning: File upload error
    deducmsV5.7 在{dede:datalist}标签中runphp无效的解决办法
    dedecmsV5.7 插入记录并返回刚插入数据的自增ID
    dedecmsV5.7 调用其他站点的数据库的数据的方法
    dedecmsV5.7 arclist 如何调用副栏目的文章
  • 原文地址:https://www.cnblogs.com/snake107/p/12118018.html
Copyright © 2020-2023  润新知