SpringBoot自动装配原理之@Import注解解析
文章目录
- 1. 概述
- 2. 使用
- 2.1 导入普通Bean
- 2.2 导入配置类
- 2.3 导入 ImportSelector 实现类
- 2.4 导入 ImportBeanDefinitionRegistrar 实现类
- 3. 区别
1. 概述
当谈及现代Java开发领域中的框架选择时,SpringBoot无疑是无与伦比的热门之选。其简化了开发流程,提高了效率,同时保持了可靠性和可维护性。在这个充满活力的生态系统中,@Import注解闪耀着独特的光芒。它不仅仅是一个普通的注解,更是一个强大的工具,为我们提供了一种优雅而灵活的方式来管理组件的装配和配置。本文将带您深入探究@Import注解的内涵,揭示其背后的原理和机制,助您在SpringBoot项目中游刃有余地应用这一技术,从而让您的应用更加强大、灵活和易于扩展。让我们一起踏上这场关于@Import注解的探索之旅,探索其奥秘,挖掘其潜力!
Spring 提供的@Import 注解: 使用@Import导入的类会被Spring加载到 IOC 容器中, 有四种用法:
- 导入Bean
- 导入配置类
- 导入 ImportSelector 实现类。一般用于加载配置文件中的类
- 导入 ImportBeanDefinitionRegistrar 实现类
2. 使用
2.1 导入普通Bean
会自动执行当前类的构造方法创建对象,存到IOC容器, bean名称为:类的全路径
构造一个类
public class User { private Integer id; private String name; // get set 略 @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
使用第一种方式导入
package com.snow; import com.snow.po.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Import; @Import(User.class)//会自动执行当前类的构造方法创建对象,存到IOC容器, bean名称为:类的全路径 @SpringBootApplication public class TempApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(TempApplication.class, args); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); // 获取并使用每个bean for (String beanName : beanDefinitionNames) { Object bean = applicationContext.getBean(beanName); System.out.println("Bean: " + beanName + " - " + bean.getClass().getName()); // 在这里可以进行进一步的操作 } // 关闭应用程序上下文 applicationContext.close(); } }
执行结果如下:
可以看到 User 类被注入了容器.
2.2 导入配置类
创建 MyConfig bean,并且类中有 带有@Bean注解方法,创建对象存到IOC容器,bean名称为:默认方法名称
定义一个类
public class Student { private Integer id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
创建配置类
@Configuration public class MyConfig { @Bean public Student student() { return new Student(); } }
第二种方式注入
//@Import(User.class)//会自动执行当前类的构造方法创建对象,存到IOC容器, bean名称为:类的全路径 @Import(MyConfig.class) // 创建MyConfig bean,并且类中有 带有@Bean注解方法,创建对象存到IOC容器,bean名称为:默认方法名称 @SpringBootApplication public class TempApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(TempApplication.class, args); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); // 获取并使用每个bean for (String beanName : beanDefinitionNames) { Object bean = applicationContext.getBean(beanName); System.out.println("Bean: " + beanName + " - " + bean.getClass().getName()); // 在这里可以进行进一步的操作 } // 关闭应用程序上下文 applicationContext.close(); } }
执行并查看结果:
可以看到注入进来了配置类本身及其配置类里面声明的bean.
2.3 导入 ImportSelector 实现类
@ImportSelector 是 Spring 框架提供的一个接口,用于动态地选择需要导入的配置类。通过实现该接口,可以根据特定的条件在运行时选择性地导入一组配置类,从而实现更加灵活的配置管理。
具体来说,@ImportSelector 接口定义了一个方法 selectImports(),该方法返回一个字符串数组,数组中包含需要导入的配置类的全限定类名。当在 Spring 配置类中使用 @Import 注解并指定实现了 @ImportSelector 接口的类时,Spring 在加载配置类时会调用该类的 selectImports() 方法,根据方法返回的配置类的全限定类名来加载相应的配置类。
使用 @ImportSelector 的主要优势在于可以根据程序运行时的条件来动态地选择性地导入配置类,从而实现更加灵活的组件管理和配置。例如,可以根据环境变量、配置文件的内容或者其他动态条件来决定需要加载哪些配置类,从而实现不同环境下的定制化配置。
定义一个类
public class Teacher { private Integer id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
编写 ImportSelector 的实现类: MyImportSelector如下:
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { // 返回类的全路径的字符串数组 return new String[]{"com.snow.po.Teacher"}; } }
第三张方式注入
//@Import(User.class)//会自动执行当前类的构造方法创建对象,存到IOC容器, bean名称为:类的全路径 //@Import(MyConfig.class) // 创建MyConfig bean,并且类中有 带有@Bean注解方法,创建对象存到IOC容器,bean名称为:默认方法名称 @Import(MyImportSelector.class) // 会调用 MyImportSelector 类的 selectImports() 方法,该方法返回一个类名称的数组,Spring 将加载这些类,这样它们的配置信息就可以被应用到当前的上下文中。 @SpringBootApplication public class TempApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(TempApplication.class, args); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); // 获取并使用每个bean for (String beanName : beanDefinitionNames) { Object bean = applicationContext.getBean(beanName); System.out.println("Bean: " + beanName + " - " + bean.getClass().getName()); // 在这里可以进行进一步的操作 } // 关闭应用程序上下文 applicationContext.close(); } }
执行并看结果:
注意: 这种方式是最重要的, SpringBoot自动装配核心使用了这种方式自动注入大量Bean.
2.4 导入 ImportBeanDefinitionRegistrar 实现类
导入 ImportBeanDefinitionRegistrar 实现类的作用是允许在 Spring 应用程序上下文加载时以编程方式注册额外的 bean 定义。与 ImportSelector 类似,ImportBeanDefinitionRegistrar 接口也提供了一种动态注册 bean 定义的机制,但是它更加灵活,可以在注册过程中对 bean 定义进行更复杂的操作。
当你使用 @Import 注解导入实现了 ImportBeanDefinitionRegistrar 接口的类时,Spring 在加载配置类时会调用该类的 registerBeanDefinitions() 方法。在这个方法中,你可以通过编程方式注册 bean 定义,包括指定 bean 的名称、类型、作用域以及其他属性。
通过使用 ImportBeanDefinitionRegistrar,你可以在运行时根据需要动态地注册 bean,这样可以更灵活地管理和配置 Spring 应用程序的组件。这种机制特别适用于需要根据外部条件或者动态变化的情况下注册 bean 的场景,例如基于配置文件或者运行时环境来决定需要注册哪些 bean。
定义一个类
public class School { private Integer id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
实现ImportBeanDefinitionRegistrar 接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * @param importingClassMetadata 导入类的元注解信息 * @param registry Bean注册表 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .rootBeanDefinition(School.class).getBeanDefinition(); registry.registerBeanDefinition("school", beanDefinition); } }
第四种方式导入
@Import(MyImportBeanDefinitionRegistrar.class) @SpringBootApplication public class TempApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(TempApplication.class, args); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); // 获取并使用每个bean for (String beanName : beanDefinitionNames) { Object bean = applicationContext.getBean(beanName); System.out.println("Bean: " + beanName + " - " + bean.getClass().getName()); // 在这里可以进行进一步的操作 } // 关闭应用程序上下文 applicationContext.close(); } }
执行看结果
可以看到注册成功.
3. 区别
@Import(MyImportSelector.class) 和 @Import(MyImportBeanDefinitionRegistrar.class) 都是用于在 Spring 中动态导入配置信息的注解,但它们的作用和使用场景略有不同。
-
@Import(MyImportSelector.class):
- 作用:通过实现 ImportSelector 接口的类,动态地选择需要导入的配置类。
- 使用场景:通常用于根据某些条件动态地选择需要导入的配置类。例如,根据环境变量或者配置文件中的设置来决定需要加载哪些组件或配置。
-
@Import(MyImportBeanDefinitionRegistrar.class):
- 作用:通过实现 ImportBeanDefinitionRegistrar 接口的类,以编程方式注册额外的 bean 定义。
- 使用场景:通常用于需要根据运行时动态条件注册 bean 的场景。例如,根据系统配置或者运行时环境来决定需要注册哪些 bean,或者根据一些复杂的逻辑来注册 bean。
使用场景示例:
如果需要根据一些条件在运行时动态地选择性地加载一些配置类,可以使用@Import(MyImportSelector.class)。
如果需要在运行时根据条件注册一些 bean,可能需要进行一些复杂的逻辑判断,并且注册的 bean 可能不只是简单地选择性加载配置类,你可以使用 @Import(MyImportBeanDefinitionRegistrar.class)。