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的工具类生成实例对象
}
相关推荐
GJCTYU3 分钟前
spring中@Transactional注解和事务的实战理解附代码
数据库·spring boot·后端·spring·oracle·mybatis
艾迪的技术之路14 分钟前
redisson使用lock导致死锁问题
java·后端·面试
今天背单词了吗98032 分钟前
算法学习笔记:8.Bellman-Ford 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·后端·算法·最短路径问题
天天摸鱼的java工程师34 分钟前
使用 Spring Boot 整合高德地图实现路线规划功能
java·后端
东阳马生架构1 小时前
订单初版—2.生单链路中的技术问题说明文档
java
咖啡啡不加糖1 小时前
暴力破解漏洞与命令执行漏洞
java·后端·web安全
风象南1 小时前
SpringBoot敏感配置项加密与解密实战
java·spring boot·后端
DKPT1 小时前
Java享元模式实现方式与应用场景分析
java·笔记·学习·设计模式·享元模式
Percep_gan1 小时前
idea的使用小技巧,个人向
java·ide·intellij-idea