@Import出现的背景
目前为止,注解的方式批量注册bean,前面2篇文章中,我们介绍了2种方式:
到目前,我们知道的批量定义bean的方式有2种:
@Configuration结合@Bean注解的方式
@CompontentScan扫描包的方式
下面我们来看几个问题。
问题1
如果需要注册的类是在第三方的jar中,那么我们如果想注册这些bean有2种方式:
通过@Bean标注方法的方式,一个个来注册
@CompontentScan的方式:默认的@CompontentScan是无能为力的,默认情况下只会注册@Compontent标注的类,此时只能自定义@CompontentScan中的过滤器来实现了
这2种方式都不是太好,每次有变化,调整的代码都比较多。
问题2
通常我们的项目中有很多子模块,可能每个模块都是独立开发的,最后通过jar的方式引进来,每个模块中都有各自的@Configuration、@Bean标注的类,或者使用@CompontentScan标注的类,被@Configuration、@Bean、@CompontentScan标注的类,我们统称为bean配置类,配置类可以用来注册bean,此时如果我们只想使用其中几个模块的配置类,怎么办?
@Import可以很好的解决这2个问题,下面我们来看@Import怎么玩的。
@Import使用
先看Spring对它的注释,总结下来作用就是和xml配置的 标签作用一样,允许通过它引入@Configuration标注的类 , 引入ImportSelector接口和ImportBeanDefinitionRegistrar接口的实现,也包括 @Component注解的普通类。
总的来说:@Import可以用来批量导入需要注册的各种类,如普通的类、配置类,完后完成普通类和配置类中所有bean的注册。
@Import的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
@Import可以使用在任何类型上,通常情况下,类和注解上用的比较多。
value:一个Class数组,设置需要导入的类,可以是@Configuration标注的列,可以是ImportSelector接口或者ImportBeanDefinitionRegistrar接口类型的,或者需要导入的普通组件类。
使用步骤
- 将@Import标注在类上,设置value参数
- 将@Import标注的类作为AnnotationConfigApplicationContext构造参数创建AnnotationConfigApplicationContext对象
- 使用AnnotationConfigApplicationContext对象
@Import的value常见的有5种用法
- value为普通的类
- value为@Configuration标注的类
- value为@CompontentScan标注的类
- value为ImportBeanDefinitionRegistrar接口类型
- value为ImportSelector接口类型
- value为DeferredImportSelector接口类型
value为普通的类
总配置类:使用@Import标注
import org.springframework.context.annotation.Import;
@Import({
Service1.class, Service2.class})
public class MainConfig1 {
}
@Import中导入了2个普通的类:Service1、Service2,这两个类会被自动注册到容器中
我们也可以指定被导入类的bean名称,使用@Compontent注解就可以了,如下:
@Component("service1")
public class Service1 {
}
value为@Configuration标注的配置类
项目比较大的情况下,会按照模块独立开发,每个模块在maven中就表现为一个个的构建,然后通过坐标的方式进行引入需要的模块。
假如项目中有2个模块,2个模块都有各自的配置类,如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 模块1配置类
*/
@Configuration
public class ConfigModule1 {
@Bean
public String module1() {
return "我是模块1配置类!";
}
}
模块2的配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 模块2配置类
*/
@Configuration
public class ConfigModule2 {
@Bean
public String module2() {
return "我是模块2配置类!";
}
}
总配置类:通过@Import导入2个模块的配置类
import org.springframework.context.annotation.Import;
/**
* 通过Import来汇总多个@Configuration标注的配置类
*/
@Import({
ConfigModule1.class, ConfigModule2.class}) //@1
public class MainConfig2 {
}
@1导入了2个模块中的模块配置类,可以按需导入。
value为@CompontentScan标注的类
项目中分多个模块,每个模块有各自独立的包,我们在每个模块所在的包中配置一个@CompontentScan类,然后通过@Import来导入需要启用的模块。
/**
* 通过@Import导入多个@CompontentScan标注的配置类
*/
@Import({
CompontentScanModule1.class, CompontentScanModule2.class}) //@1
public class MainConfig3 {
}
@1导入了2个模块中的组件扫描类,可以按需导入。
ImportBeanDefinitionRegistrar接口
这个接口提供了通过spring容器api的方式直接向容器中注册bean。
接口的完整名称:
org.springframework.context.annotation.ImportBeanDefinitionRegistrar
源码:
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
2个方法中主要有3个参数
importingClassMetadata
AnnotationMetadata类型的,通过这个可以获取被@Import注解标注的类所有注解的信息。registry
BeanDefinitionRegistry类型,是一个接口,内部提供了注册bean的各种方法。importBeanNameGenerator
BeanNameGenerator类型,是一个接口,内部有一个方法,用来生成bean的名称。
关于BeanDefinitionRegistry和BeanNameGenerator接口在来细说一下。
BeanDefinitionRegistry接口:bean定义注册器
bean定义注册器,提供了bean注册的各种方法,来看一下源码:
public interface BeanDefinitionRegistry extends AliasRegistry {
/**
* 注册一个新的bean定义
* beanName:bean的名称
* beanDefinition:bean定义信息
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws