不是银趴~是@Import!

怀念夏天  金牌会员 | 2024-3-28 10:08:58 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 891|帖子 891|积分 2673

首先我们要明确:@Import 注解是 Spring 提供的。
然后我们看一下该注解的官方注释:
  1. Indicates one or more component classes to import — typically @Configuration classes.
  2. Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register).
  3. @Bean definitions declared in imported @Configuration classes should be accessed by using @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 @Configuration class methods.
  4. May be declared at the class level or as a meta-annotation.
  5. If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.
复制代码
我们将其中我们关心的重要部分提取出来翻译整理如下:

  • 该注解可导入一个或多个组件类——通常是Configuration类。
  • 允许导入@Configuration类、ImportSelector接口和ImportBeanDefinitionRegistrar接口的实现,以及普通类(从4.2版本开始)。
  • @Import注解可以在类上声明,也可以作为元注解声明在其他注解上。
那么 @Import 注解到底有什么作用和优势呢?
记住下面几句话:
导入配置。
从各个地方、通过各种方式导入配置。
在你需要的时候,从各个地方、通过各种方式导入配置。
在你需要的时候,从各个地方、通过各种方式导入并改造成你喜欢的配置。
下面我们通过示例讲 4 种官方注释给出的导入类型,分别是:

  • 普通类
  • @Configuration类
  • ImportSelector接口实现
  • ImportBeanDefinitionRegistrar接口实现
普通类

普通类不必啰嗦,有手就行。
  1. public class Circle {
  2. }
复制代码
  1. @Configuration
  2. @Import({Circle.class})
  3. public class MainConfig {
  4. }
复制代码
导入@Configuration类

我们建立三个不同的模块,MainModule, Module1, Module2。
让 MainModule 引入 Module1、Module2依赖。
在 MainModule 中创建 MainConfig 类如下:
  1. @Configuration
  2. @Import({Config1.class, Config2.class})
  3. public class MainConfig {
  4. }
复制代码
很明显,Config1,Config2 分别属于2个模块,如下:
  1. @Configuration
  2. public class Config1 {
  3.     @Bean
  4.     public String config1() {
  5.         return "我是config1配置类!";
  6.     }
  7. }
复制代码
  1. @Configuration
  2. public class Config2 {
  3.     @Bean
  4.     public String config2() {
  5.         return "我是config2配置类!";
  6.     }
  7. }
复制代码
在 MainModule 中创建测试类看是否生效:
  1. @Test
  2. void contextLoads() {
  3.     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
  4.     for (String name : context.getBeanDefinitionNames()) {
  5.         System.out.println(String.format("%s->%s", name, context.getBean(name)));
  6.     }
  7. }
复制代码
测试结果如下:
  1. mainConfig->com.example.annotation.config.MainConfig$$EnhancerBySpringCGLIB$$a98d5b6a@55a609dd
  2. com.example.module1.config1->com.example.module1.config.Config1$$EnhancerBySpringCGLIB$$72aca5d0@4afd21c6
  3. config1->我是config1配置类!
  4. com.example.module2.config2->com.example.module2.config.Config2$$EnhancerBySpringCGLIB$$72aca5d0@4afd21c6
  5. config2->我是config2配置类!
复制代码
ImportSelector接口实现

首先我们需要认识一下 ImportSelector 接口:
  1. public interface ImportSelector {
  2.     String[] selectImports(AnnotationMetadata importingClassMetadata);
  3.     @Nullable
  4.     default Predicate<String> getExclusionFilter() {
  5.         return null;
  6.     }
  7. }
复制代码
只有一个需要实现的方法:
selectImports
参数 AnnotationMetadata 是什么?
翻译为注解元数据。
有什么用?
用来获取被@Import标注的类上面所有的注解信息。
String[] 是什么?
返回需要导入的类名的数组,可以是任何普通类,配置类。
上示例:
创建一个配置类
  1. @Configuration
  2. public class Config {
  3.     @Bean
  4.     public String name() {
  5.         return "公众号:JavaCode";
  6.     }
  7. }
复制代码
定义一个ImportSelectors实现类
  1. public class MyImportSelector implements ImportSelector {
  2.     @Override
  3.     public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  4.         return new String[]{
  5.                 Config.class.getName()
  6.         };
  7.     }
  8. }
复制代码
上述代码中需要说明一下,Config.class.getName() 得到的是类的全类名,也就是[包名.类名],也就是com.example.annotation.pojo.Config。
导入MyImportSelector
  1. @Import({MyImportSelector.class})
  2. public class MainConfig {
  3. }
复制代码
这时候有人问了:
这里怎么没有 @Configuration 注解?
没关系哈,虽然 @Import 注解通常是与 @Configuration 注解一起使用的,但不是绝对必要的。
ImportBeanDefinitionRegistrar接口实现

首先我们还是先认识一下 ImportBeanDefinitionRegistrar 接口:
  1. public interface ImportBeanDefinitionRegistrar {
  2.     default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
  3.         registerBeanDefinitions(importingClassMetadata, registry);
  4.     }
  5.     default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  6.     }
  7. }
复制代码
可以看到只有2个方法,第一个方法调用第二个方法,所以我们就只看第二个方法就好了。
AnnotationMetadata 我们前边说过了:
用来获取被@Import标注的类上面所有的注解信息。
BeanDefinitionRegistry 呢?
它是一个接口,内部提供了注册bean的各种方法,用于我们手动注册bean。
上示例:
创建一个 Java 类
  1. public class Rectangle {
  2.     public void sayHi() {
  3.         System.out.println("Rectangle sayHi()");
  4.     }
  5. }
复制代码
创建 ImportBeanDefinitionRegistrar 实现类
  1. public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  2.     @Override
  3.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  4.         RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rectangle.class);
  5.         // 注册一个名字叫做 rectangle 的 bean
  6.         registry.registerBeanDefinition("rectangle", rootBeanDefinition);
  7.     }
  8. }
复制代码
总结

第一种:
我们有什么就可以导什么。
第二种:
我们可以把分散的配置集中起来,更加清晰和有组织。
第三种:
除了我们模块现有的配置,你只要能拿到类的全类名,就能注册进来。
第四种:
我们不光能到处拿,我们还能拿来以后随便改!
以上四种方式就实现了我们当初的豪言:
导入配置。
从各个地方、通过各种方式导入配置。
在你需要的时候,从各个地方、通过各种方式导入配置。
在你需要的时候,从各个地方、通过各种方式导入并改造成你喜欢的配置。
● 终于搞懂动态代理了!
● 学会@ConfigurationProperties月薪过三千
● 学一点关于JVM类加载的知识
● Java注解,看完就会用
● Java反射,看完就会用
● @Value是个什么东西


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

怀念夏天

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表