一、@Import 注解介绍
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* value 值可以是 @Configuration注解类,或者 ImportSelector 以及 ImportBeanDifinitionRegistrar 的实现类,或者一些常规的组件类
*/
Class<?>[] value();
}
- @Import 注解可用于导入一个或多个组件类(一般是 @Configuration 类)。
- @Import 注解提供了与 Spring XML 中的 <limport> 元素等效的功能。允许导入@Configuration 类、ImportSelector 和 ImportBeanDefinitionRegistrar 实现类,以及常规组件类。
二、使用 Import 给容器中快速导入一个组件
创建实体类 Color
package org.example.pojo;
public class Color {
}
创建实体类 Red
package org.example.pojo;
public class Red {
}
创建配置类 ImportTestConfig,使用 @Import 注解向容器中注入 Color 以及 Red 组件
package org.example.config;
import org.example.pojo.Color;
import org.example.pojo.Red;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({Color.class, Red.class})
@Configuration
public class ImportTestConfig {
}
测试方法:
@Test
public void testImport(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ImportTestConfig.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
通过 @Import 注解成功的将 Color 以及 Red 两个实体类注入到了容器中,其在容器中的 id 默认是实体类的全类名
三、使用 ImportSelector 导入一组组件
3.1 接口定义
ImportSelector 是一个接口,先来看一下这个接口的定义:
public interface ImportSelector {
/**
* 根据导入的 @Configuration 类的 AnnotationMetadata 选择并返回应导入的类的名称。
* @return 返回需要导入的类名,如果没有则返回一个空数组
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
/**
* 用于排除某些不需要导入容器的候选类
*/
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
AnnotationMetadata 接口可以用来获取当前标注 @Import 注解的类的所有注解信息。
package org.springframework.core.type;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
/**
* 获取基础类上存在的所有注释类型的完全限定类名。
*/
default Set<String> getAnnotationTypes() {
return getAnnotations().stream()
.filter(MergedAnnotation::isDirectlyPresent)
.map(annotation -> annotation.getType().getName())
.collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
* 根据给定的注解名获取类上所有的元注解类型
*/
default Set<String> getMetaAnnotationTypes(String annotationName) {
MergedAnnotation<?> annotation = getAnnotations().get(annotationName, MergedAnnotation::isDirectlyPresent);
if (!annotation.isPresent()) {
return Collections.emptySet();
}
return MergedAnnotations.from(annotation.getType(), SearchStrategy.INHERITED_ANNOTATIONS).stream()
.map(mergedAnnotation -> mergedAnnotation.getType().getName())
.collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
* 确定基础类是否使用了给定类型的注解
*/
default boolean hasAnnotation(String annotationName) {
return getAnnotations().isDirectlyPresent(annotationName);
}
/**
* 确定基础类是否有一个注释,该注释本身使用给定类型的元注释进行注释。
*/
default boolean hasMetaAnnotation(String metaAnnotationName) {
return getAnnotations().get(metaAnnotationName,
MergedAnnotation::isMetaPresent).isPresent();
}
/**
* 确定基础类是否具有使用给定注释类型注释(或元注释)的方法。
*/
default boolean hasAnnotatedMethods(String annotationName) {
return !getAnnotatedMethods(annotationName).isEmpty();
}
/**
* 获取所有使用给定注释类型注释(或元注释)的所有方法的方法元数据。
*/
Set<MethodMetadata> getAnnotatedMethods(String annotationName);
/**
* 使用标准反射为给定类创建新 AnnotationMetadata 实例的工厂方法
*/
static AnnotationMetadata introspect(Class<?> type) {
return StandardAnnotationMetadata.from(type);
}
}
3.2、案例
创建实体类 Yellow 以及 Blue
Yellow
package org.example.pojo;
public class Yellow {
}
Blue
package org.example.pojo;
public class Blue {
}
通过 @ImportSelector 注解的方式将 Yellow 和 Blue 这两个实体类注入到容器中
实现 ImportSelector 接口
package org.example.condition;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Set;
public class MyImportSelector implements ImportSelector {
/**
* AnnotationMetadata:用于获取当前标注 @Import 注解的类的所有注解信息
* */
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"org.example.pojo.Blue","org.example.pojo.Yellow"};
}
}
在配置类 @Import 注解上,新增 MyImportSelector.class
package org.example.config;
import org.example.condition.MyImportSelector;
import org.example.pojo.Color;
import org.example.pojo.Red;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({Color.class, Red.class, MyImportSelector.class})
@Configuration
public class ImportTestConfig {
}
测试方法:
@Test
public void testImport(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ImportTestConfig.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
根据测试结果可以看出,Blue 和 Yellow 两个实体类已经注册成功。
四、使用 ImportBeanDefinitionRegistrar 导入
4.1 ImportBeanDefinitionRegistrar 接口定义:
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
/**
* 用于注册 beanDifinition
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDifinition注册类
*/
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
4.2 案例
实体类 Rainbow
package org.example.pojo;
public class Rainbow {
}
MyImportBeanDefinitionRegistrar 类:
package org.example.condition;
import org.example.pojo.Rainbow;
import org.springframework.beans.factory.config.BeanDefinition;
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 MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDifinition注册类
*
* 把所有需要添加到容器中的 bean,通过调用
* BeanDefinitionRegistry.registerBeanDifinition 手工注册进来
* @param importingClassMetadata
* @param registry
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("org.example.pojo.Red");
boolean definition2 = registry.containsBeanDefinition("org.example.pojo.Blue");
if(definition && definition2){
RootBeanDefinition beanDefinition = new RootBeanDefinition(Rainbow.class);
registry.registerBeanDefinition("rainbow", beanDefinition);
}
}
}
在配置类的 @Import 注解上新增定义的 MyImportBeanDefinitionRegistrar 类
package org.example.config;
import org.example.condition.MyImportBeanDefinitionRegistrar;
import org.example.condition.MyImportSelector;
import org.example.pojo.Color;
import org.example.pojo.Red;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportTestConfig {
}
测试方法:
@Test
public void testImport(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ImportTestConfig.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
总结
@Import 注解向容器中注入 bean 的方式有三种,一是直接导入普通类或者配置类,二是通过导入实现了 ImportSelector 接口的类来实现批量导入 bean 实例,三是通过导入实现了 ImportBeanDefinitionRegistrar 接口来实现导入 bean 实例。