Import注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
//Import注解常用于导入spring component
Class<?>[] value();
}
@Import注解的使用主要与ConfiguretionClassPostProcessor有关。ConfigurationClassPostProcessor是Spring在启动过程中加载到ioc中来的,其实质是一个BeanFactoryPostProcessor(BeanFactoryPostProcessor是Spring提供的扩展点,可以实现此接口中的方法来改变beanFactory中BeanDefinition的定义)
//configClass是配置类,一般是具有@Configuration注解的类
//importCandidates是@Import注解导入的类
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//第一种情况:导入的类是ImportSelector类型
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateCla
ss(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
第二种情况:导入的类是ImportBeanDefinitionRegistrar类型
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 第三种情况,以上两种情况都不是,即当作@Configuration处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
//**
finally {
this.importStack.pop();
}
}
}
导入的类通常有以下三种类型
- ImportSelector类型
- ImportBeanDefinitionRegistrar类型
- 普通的bean组件
注意:导入的类如果是前两种类型,则类本身并不会被注册到spring中,而被注册的类只是其 想注册的类
1.1. ImportSelector接口
spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在。通过Import注解导入其实现类,可以实现导入组件的功能。SpringBoot的自动装配大多都是基于此机制。
public interface ImportSelector {
//需要实现的方法,返回的是一个字符串数组,每个字符串都是一个全类名
//Spring会根据这些全类名将相应的类加入到Ioc中管理
//AnnotationMetadata继承ClassMetadata和AnnotatedTypeMetadata接口,其可以获取类信息和类上所有的注解信息
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter(){
return null;
}
}
根据ImportSelector接口的作用,我们可以自定义一个包含其他信息注解,并且注解中嵌入@Import注解,我们就可以通过Import注解导入一个实现了ImportSelector的类,在这个类中我们通过获取我们自定义注解的相关信息,来返回我们想要注册的类的全类名,从而自定义了一个@ComponentScan注解
SpringBoot中的应用
SpringBoot项目中启动类带有@SpringBootApplication注解,其中就包含了一个@EnableAutoConfiguration注解,其内部就是采用上面的方式Import了一个ImportSelector是实现类,其中通过获取类路径下Spring.factories文件中的配置信息来加载自动配置类,进而实现自动配置的。
另外很多@Enablexxxx注解都是通过这种机制来实现某功能的自动配置的
示例
public class AppImportSelectorDemo implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
System.out.println("\n@Import AppImportSelectorDemo所在类上的注解信息");
for (String annotationType : annotationTypes) {
System.out.println(annotationType);
}
System.out.println("------------------------------------------------------");
return new String[]{DemoClzz.class.getName()};
}
}
public class DemoClzz {
private String desc = "test Demo classs";
@PostConstruct
public void test() {
System.out.println(desc + " has init");
}
}
@Configuration
@Import(AppImportSelectorDemo.class)
public class AppConfig {
}
/**输出
@Import AppImportSelectorDemo所在类上的注解信息
org.springframework.context.annotation.Configuration
org.springframework.context.annotation.Import
------------------------------------------------------
test Demo classs has init
**/
1.2. ImportBeanDefinitionRegistrar接口
public interface ImportBeanDefinitionRegistrar {
//在类的注解信息的基础上注册新beanDefinition
//实际上跟ImportSelector接口差不多的,但是相比较来说ImportSelector更加自动(根据类名自动创建Defnition),而ImportBeanDefinitionRegistrar提供了更加广泛的接口,需要自己创建BeanDefinition给予了开发者更大的发挥空间
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
Spring中的应用
@EnableConfigurationProperties注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
EnableConfigurationPropertiesRegistrar就实现了ImportBeanDefinitionRegistrar接口,其将获取@EnableConfigurationProperties注解信息也就是属性配置类放入ioc容器中进行管理
*/
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
Class<?>[] value() default {};
}
mybatis中的应用
@MapperScan注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//导入了MapperScannerRegistrar类,实现了ImportBeanDefinitionRegistrar接口,并创建MapperScannerConfigurer对应的BeanDefinition,并将从获取MapperScan中的注解信息比如basePackages,存入到BeanDefinition中,最终注册到spring中
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
/**
省略
*/
}
1.3 import普通类
当Import注解导入普通类的时候它会将普通的类当作@Configuration修饰的类,即当作配置类来处理
在普通类中还有一个特殊的类型:实现了ImportAware接口的类
public interface ImportAware extends Aware {
//它会将相对应的类和类上的注解信息注入过来
void setImportMetadata(AnnotationMetadata importMetadata);
}
特点:
- 能够交给ioc来管理
- 可以获取Import注解使用位置的类信息
我们利用其能够获取注解信息且能够被注册到ioc的这个性质,就可以开发特定的小注解,以实现特定的功能