Spring-ImportSelector接口功能介绍

ImportSelector接口是至spring中导入内部类或者外部类的核心接口,只需要其定义的方法内返回需要创建bean的class字符串就好了,比如:当我们引入一个外部share包,我们拿到里面的Class返回出去,就能得到这个bean,是多么神奇的事情,前提是这个类不是接口哦。

ImportSelector往往结合@Import注解一起使用,可以参考我的这篇文章@Import注解介绍

java 复制代码
public interface ImportSelector {
	//返回类的字符串数组,也就是要创建的bean的className,比如userService.class.getName()
    //被创建的bean是在其他bean(@Component、@Service等注解修饰的Bean)创建之前创建的
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

二、使用案例

通过返回Class的字符串来创建bean

java 复制代码
//定义一个业务类
public class UserServiceTest {
	public String getUserName(){
		return "测试";
	}
}

//实现ImportSelector接口
public class MyImportSelect implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//返回要注册到Spring容器的Class集合
		return new String[]{UserServiceTest.class.getName()};
	}
}

//配置类
@Configuration
@Import(MyImportSelect.class) //导入我们实现ImportSelector的类。
public class AppConfigClassTest {
}

测试

java 复制代码
public class MainTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class);
		//这里我们不能通过"userServiceTest"来获取bean,因为这个bean的name不是userServiceTest,而是userServiceTest.getClass().getName()
        //因为bean的别名成功器,只是针对像注解@Service等注解,会生成一个首字母小写的BeanName
		UserServiceTest userServiceTest = AnnotationConfigApplicationContext.getBean(UserServiceTest.class);
		System.out.println(((UserServiceTest)userServiceTest).getUserName());
	}
}

下面说下源码是怎么实现的,可以跳转到@Import注解介绍

java 复制代码
//这里就直接跳到ConfigurationClassPostProcessor处理@Import注解的逻辑上
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, 
                Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {

    //如果importCandidates为空直接return,为什么会有这个,因为下面代码可能会递归调用processImports,就比如Import一个类,这个类也带了@Import注解,那就会在调用一次processImports方法
    if (importCandidates.isEmpty()) {
        return;
    }

    for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
        	//1、import的类,实现了ImportSelector接口
            Class<?> candidateClass = candidate.loadClass();
            //利用反射Class实例化对象,这个对象不是代理对象不要搞混了。
            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) {
                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            } else {
            	//调用ImportSelector接口里面的selectImports方法,拿到返回值Class集合。在通过递归的方式嗲用processImports挨个解析每一个Class
                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                //转成SourceClass集合
                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                //再次调用processImports方法
                processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
        } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
           。。。。。。
        } else {
            //3、ImportSelector和ImportBeanDefinitionRegistrar都没有实现
            this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            //进一步解析其他注解,比如@Component @Import等最后会把configClass注册到Spring容器中。
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}

看下instantiateClass方法做了什么

java 复制代码
//创建实例对象
static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment, ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {

	ClassLoader classLoader = 
	       (registry instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());

	T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
	ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
	return instance;
}

//调用createInstance方法创建实例对象
private static Object createInstance(Class<?> clazz, Environment environment, ResourceLoader resourceLoader, BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {
	Constructor<?>[] constructors = clazz.getDeclaredConstructors();
	。。。。。。。
	return BeanUtils.instantiateClass(clazz);//通过Bean的工具类生成实例对象
}
相关推荐
计算机学姐9 分钟前
基于SpringBoot的高校竞赛管理系统
java·spring boot·后端·spring·信息可视化·tomcat·mybatis
AnalogElectronic11 分钟前
普通数据源和druid数据源区别以及druid参数详解
java
東雪木14 分钟前
Java学习——泛型基础:泛型的核心作用、泛型类 / 方法 / 接口的定义
java·学习·java面试
一叶飘零_sweeeet19 分钟前
ConcurrentHashMap 深度解析:从 JDK7 到 JDK8 的演进与并发安全保障
java·并发编程
三原19 分钟前
超级好用的三原后台管理v1.0.0发布🎉(Vue3 + Ant Design Vue + Java Spring Boot )附源码
java·vue.js·开源
文慧的科技江湖20 分钟前
光储充协同的终极闭环:用SpringCloud微服务打造“发-储-充-用“智能能源网络 - 慧知开源充电桩管理平台
java·开发语言·spring cloud·微服务·能源·充电桩开源平台·慧知重卡开源充电桩平台
東雪木24 分钟前
Java学习——内部类(成员内部类、静态内部类、局部内部类、匿名内部类)的用法与底层实现
java·开发语言·学习·java面试
满满和米兜28 分钟前
【Java基础】-I/O-字符流
java·开发语言·python
huanmieyaoseng100330 分钟前
SpringBoot使用Redis缓存
java·spring boot·后端
小小仙。31 分钟前
IT自学第三十八天
java·开发语言