@Import的使用和源码分析

在日常开发中我们可能需要动态的来加载某些功能,而且需要方便的开启关闭和整合,那么这个时候我们就可以利用 @Import这个注解来实现这一特性。

@Import的使用实例

以Mybatis为例子,在我们想使用Mybatis的时候可以可选的方法有在启动来上加@MapperSacn这个注解来进行Mybatis的整合,如果不想用我们也只需要把这个注解去掉就可以。那我如果我也想有这样的特性我们应该怎么做呢,首先@Import是一个需要添加在类上面的注解而且在运行阶段是可以解析到的(如果这个你不太懂为什么请看@Import注解类的源码和这篇文章),这个注解可以放到另一个注解类上例如HxlEnable,这里注解里面有一个参数(Class<?>[] value()) 这个地方我们有三种方式使用它。

1、可以传一个类或者一堆类进去例如@Import({A.class,B.class}) 他会把这两个当做bean添加到beanFactory中让我们在其他地方注入

@Import(TestImport.class) //这个注解被加到了另一个注解的类上面 当然它不只是可以添加到注解类上
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HxlEnable { //定义了一个HxlEnable注解
}

@Slf4j
public class TestImport { //等待被注入的bean
    public void hello() {
      log.info("hello");
    }
}

@HxlEnable //自定义的标签
public class DemoApplication { //启动类
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

@Test
public void should_import() { //测试代码
  TestImport testImport = applicationContext.getBean(TestImport.class); //获取一个类型为TestImport的bean
  log.info("factory bean {}", testImport.getClass());
  testImport.hello();
}

上测试日志截图
测试import单个class
那这个时候有人就会问了这和我在类上面加个@Component、@Service从使用上有什么区别吗?你别着急他的作用来了,当我们把@HxlEnable从启动类上去掉后他找不到这个bean了,这就是他的作用,加不加载这个bean是看你加没加@HxlEnable注解,当你有一堆类要动态加载的时候就可以用这种方式解决。

2、ImportSelector方式这种方式会比第一种更加的实用一些,这种方法我们可以传入一个实现了ImportSelector接口的类,要动态加载的类在要实现的方法selectImports的返回值上,不多比比写个实例给你看。

public class HxlImportSelector implements ImportSelector { //实现了ImportSelector的类
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {TestImport.class.getName()}; //返回值是要动态加载的类的类名
    }
}

@Import(HxlImportSelector.class)//只有这里和第一种方法变了
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HxlEnable {
}

测试代码不变,测试依然通过了图就不放了和第一种方法的测试图相同。
3.ImportBeanDefinitionRegistrar方式这种方式需要给@Import传一个实现了ImportBeanDefinitionRegistrar接口的类,然后你可以在方法registerBeanDefinitions中得到BeanDefinitionRegistry,然后你都拿到BeanDefinitionRegistry你不就是想干嘛就干嘛,为所欲为之为所欲为。上代码

public class HxlImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestImport.class);
        registry.registerBeanDefinition("testImport", rootBeanDefinition); //给registry 里添加 一个bean
    }
}

@Import(HxlImportBeanDefinitionRegistrar.class) //只有这里变了
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HxlEnable {
}

测试代码依然能过图就不贴了,以上就是三种使用方式。

2、从源码角度分析@Import
如果你不知道spring是怎运行到处理Import注解这一步的建议看这个,我们现在就来看看spring是怎么处理@Import的,

在ConfigurationClassParser.java中调用方法processImports之前先用getImports(sourceClass)获得了当前类上加了@Import注解的元信息,在getImports方法内部有一个递归方法,这个递归方法获取了当前类上注解里面加了@Import的value里面的值,然后递归不是@Import的注解,把不是@Import的注解都解析一遍,这样一直递归下去 就找到了所有的@Import里面的value的值,这个时候是递归注解,对value里面没有处理,递归完之后传给processImports方法。

在processImports方法里,做了对value里面的值(也就是class)做了判断,
a.如果这个class实现了ImportSelector接口,他就先用ParserStrategyUtils.instantiateClass方法创建这个实例再看是否有过滤器和是否实现的DeferredImportSelector接口,如果没问题则调用这个类实现的selectImports方法拿到要注入的class文件,然后再扫描这个类上面的@Import注解调用, 其中DeferredImportSelector是ImportSelector的子类,起到延时加载的作用

b.如果没有实现ImportSelector接口,则判断是否实现了ImportBeanDefinitionRegistrar接口,如果实现了ImportBeanDefinitionRegistrar接口则创建实例并调用addImportBeanDefinitionRegistrar方法加入importBeanDefinitionRegistrar后面处理

c.如果都没实现则他会把你作为配置了,这样就回到了@Configuration的处理逻辑上他调用了processConfigurationClass方法来处理
这三个是三选一的

//ConfigurationClassParser.java
public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			if (bd instanceof AnnotatedBeanDefinition) {
				//处理非延迟的
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
			else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
			}
			else {
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
	}
	//处理延迟的 (就在这处理的延迟的那些, 下面说的是这里)
	this.deferredImportSelectorHandler.process();
}


//扫描当前类加了@Import的注解
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
	Set<SourceClass> imports = new LinkedHashSet<>();
	Set<SourceClass> visited = new LinkedHashSet<>();
	collectImports(sourceClass, imports, visited);
	return imports;
}

//这是个递归方法,扫描到注解后他又去扫描了注解的注解
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {
	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

//processImports 方法中的片段
for (SourceClass candidate : importCandidates) {
	if (candidate.isAssignable(ImportSelector.class)) { //是否实现了ImportSelector
		// Candidate class is an ImportSelector -> delegate to it to determine imports
		Class<?> candidateClass = candidate.loadClass();
		ImportSelector selector = ParserStrategyUtils.instantiateClass(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) { //如果是延迟的就放到deferredImportSelectorHandler中 延迟处理 上面有些
			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);
		}
	}
	else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
		//如果实现了ImportBeanDefinitionRegistrar就把它当成一个BeanDefinitionRegistrar处理
		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);
	}
}

至此你已经了解了@Import的处理,如果有疑问欢迎评论区或者私信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值