@Import标签,从源码详细分析使用和原理

本文详细分析了Spring框架中@Import标签的使用,包括导入ImportSelector实现类、ImportBeanDefinitionRegistrar实现类和普通类的三种情况。通过实例展示了不同返回类型对Spring容器的影响,并给出了核心源码解析。结论指出,ImportSelector和ImportBeanDefinitionRegistrar实现类自身不会放入容器,而特定条件下的普通类会被解析并放入容器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先@Import标签可以引入三种类,分别说明其作用

一.@Import(ImportSelector实现类.class)

实现接口ImportSelector,重写抽象方法selectImports,返回类全限定名字符串数组
下面针对返回类全限定名三种不同场景分析:

1.selectImports方法返回的类全限定名是普通类(即没有实现ImportSelector和ImportBeanDefinitionRegistrar接口)

①编写实现ImportSelector接口的子类MyImportSelector,重写抽象方法selectImports,返回普通类全限定名字符串

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//返回的User就是普通类
		return new String[]{"cn.zhutan.test.import_anno.User"};
	}

}


②在测试类中,用@Import标签引入MyImportSelector

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(value = {MyImportSelector.class})
public class DemoTest {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(DemoTest.class);
		User user = ac.getBean(User.class);
		System.out.println(user);
	}
}

运行结果如下,表明可以从spring已经创建User对象,并且可以从容器中获取到User对象
在这里插入图片描述
结论:
当你返回的类是普通类时,spring后续就会根据你返回的类全限定名字符串解析和创建bean定义,创建bean对象等等一系列操作,然后放到spring容器中

2.返回的类是ImportSelector子类

①编写ImportSelector的子类OtherImportSelector,重写抽象方法selectImports,返回MyImportSelector全限定名字符串

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class OtherImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//返回的MyImportSelector是实现了ImportSelector接口的类
		//而MyImportSelector的selectImports方法又返回了User类
		return new String[]{"cn.zhutan.test.import_anno.MyImportSelector"};
	}

}


②在测试类中,用@Import标签引入OtherImportSelector

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(value = {OtherImportSelector.class})
public class DemoTest {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(DemoTest.class);
		User user = ac.getBean(User.class);
		System.out.println(user);
		MyImportSelector mis = ac.getBean(MyImportSelector.class);
		System.out.println(mis);
		//OtherImportSelector ois = ac.getBean(OtherImportSelector.class);
		//System.out.println(ois);
	}
}

运行结果如下,User对象可以从spring容器中拿到,但是MyImportSelector不能从spring中拿到,同理OtherImportSelector也不能拿到
在这里插入图片描述
结论:
当返回的类是ImportSelector子类时,创建该类对象后(只是创建了临时变量),调用完selectImports方法后,对象就销毁了,其实就是局部变量而已,不会放到spring容器中

3.返回的类是ImportBeanDefinitionRegistrar子类

①编写ImportBeanDefinitionRegistrar的实现类MyImportBeanDefinitionRegistrar,重写抽象方法registerBeanDefinitions,注册了User对象

import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
		AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
		/**
		 * 注册了User对象
		 */
		registry.registerBeanDefinition("user", beanDefinition);
	}

}

②编写ImportSelector的子类AnOtherImportSelector,重写抽象方法selectImports,返回MyImportBeanDefinitionRegistrar全限定名字符串

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class AnOtherImportSelector implements ImportSelector{

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//返回实现了接口ImportBeanDefinitionRegistrar的子类MyImportBeanDefinitionRegistrar
		return new String[]{"cn.zhutan.test.import_anno.MyImportBeanDefinitionRegistrar"};
	}

}

③在测试类中,用@Import标签引入AnOtherImportSelector

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(value = {AnOtherImportSelector.class})
public class DemoTest {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(DemoTest.class);
		User user = ac.getBean(User.class);
		System.out.println(user);
		MyImportBeanDefinitionRegistrar mibdr = ac.getBean(MyImportBeanDefinitionRegistrar.class);
		System.out.println(mibdr);
	}
}



运行结果如下,可以从spring容器中获取User对象,但是不能获取MyImportBeanDefinitionRegistrar对象
在这里插入图片描述
结论:
当返回的类是ImportBeanDefinitionRegistrar子类时,创建该类对象后(只是创建了临时变量)并put到了importBeanDefinitionRegistrars这个map中,后续执行registerBeanDefinitions方法创建了User对象放到了spring容器中,但是MyImportBeanDefinitionRegistrar本身不会放到spring容器中

二.@Import(ImportBeanDefinitionRegistrar实现类.class)

①编写ImportBeanDefinitionRegistrar的实现类MyImportBeanDefinitionRegistrar,重写抽象方法registerBeanDefinitions,注册User对象

import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
		AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
		/**
		 * 注册了User对象
		 */
		registry.registerBeanDefinition("user", beanDefinition);
	}

}

②在测试类中,用@Import标签引入MyImportBeanDefinitionRegistrar

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class DemoTest {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(DemoTest.class);
		User user = ac.getBean(User.class);
		System.out.println(user);
		MyImportBeanDefinitionRegistrar mibdr = ac.getBean(MyImportBeanDefinitionRegistrar.class);
		System.out.println(mibdr);
	}
}


运行结果如下,可以从spring容器中获取User对象,但是不能获取MyImportBeanDefinitionRegistrar对象
在这里插入图片描述
结论:
实现接口ImportBeanDefinitionRegistrar,重写抽象方法registerBeanDefinitions,参数中提供了注册器,可以向容器中注册相关bean,但是从下面源码可以知道,实现ImportBeanDefinitionRegistrar的子类首先会put到importBeanDefinitionRegistrars这个map中,后续会取出importBeanDefinitionRegistrars调用registerBeanDefinitions方法,进行注册bean,MyImportBeanDefinitionRegistrar本身不会放到spring容器中

三.@Import(普通类.class)

①创建User对象

public class User {
	
}

②在测试类中,用@Import标签引入User

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(value = {User.class})
public class DemoTest {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(DemoTest.class);
		User user = ac.getBean(User.class);
		System.out.println(user);
	}
}

运行结果如下,User对象可以从spring容器拿出来
在这里插入图片描述
结论:
普通对象会解析创建然后放到spring容器中

核心源码如下:

ConfigurationClassParser解析@Import标签,下面贴出核心方法

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, 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 = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						ParserStrategyUtils.invokeAwareMethods(
								selector, this.environment, this.resourceLoader, this.registry);
						if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
							/**
							 * 如果是这个接口的实现类DeferredImportSelector,那么就添加到集合中,延迟执行
							 */
							this.deferredImportSelectors.add(
									new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
						}
						else {
							/**
							 * 这里调用了ImportSelector.selectImports方法
							 */
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					/**
					 * 处理ImportBeanDefinitionRegistrar
					 */
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						Class<?> candidateClass = candidate.loadClass();
						// delegate to it to register additional bean definitions
						ImportBeanDefinitionRegistrar registrar =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						/**
						 * 添加了ImportBeanDefinitionRegistrar到集合中,还未执行,后面会执行
						 */
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					/**
					 * 处理普通的Import
					 * 
					 * 注意:只有不实现ImportSelector和不实现ImportBeanDefinitionRegistrar的普通类才会注册解析并且注册到spring容器中
					 * (意思就是只要实现了ImportSelector和ImportBeanDefinitionRegistrar其中一个接口,即使贴上了@Configuration注解,也不解析该配置)
					 */
					else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						/**
						 * 
						 */
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

总结:

不会放到spring容器中的情况:
1.使用@Import标签引入的ImportSelector或ImportBeanDefinitionRegistrar的实现类本身不会放到spring容器中
2.使用@Import标签引入的ImportSelector实现类,对应的实现方法selectImports返回的类,如果还是ImportSelector或者ImportBeanDefinitionRegistrar实现类,也不会放到容器中

放到spring容器中的情况:
1.直接通过@Import引入没有实现ImportSelector和ImportBeanDefinitionRegistrar接口的普通类
2.使用@Import引入一个ImportSelector实现类,selectImports方法返回没有实现ImportSelector和ImportBeanDefinitionRegistrar接口的普通类全限定名字符串
3.使用@Import引入ImportBeanDefinitionRegistrar的实现类,直接用方法registerBeanDefinitions中参数registry注册器,注册bean到spring容器中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值