Spring-Import注解

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的这个性质,就可以开发特定的小注解,以实现特定的功能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值