• Spring-ImportSelector接口功能介绍


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

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

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

    二、使用案例

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

    //定义一个业务类
    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 {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试

    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());
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    //这里就直接跳到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
            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);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    看下instantiateClass方法做了什么

    //创建实例对象
    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的工具类生成实例对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    算法-排序算法
    计算机毕业设计Java创意产业园区管理(源码+系统+mysql数据库+lw文档)
    30天精通Nodejs--目录与说明
    山西电力市场日前价格预测【2023-10-04】
    内耗自救指南|5招停止内耗让你逆风翻盘
    全志V3S开发板驱动示例(linux demo驱动开发)
    【网络协议】聊聊DHCP和PXE 工作原理
    Linux虚拟机部署运行OSU Micro Benchmark
    els 方块停在方块上。
    民生银行信用卡中心金融科技24届春招面经
  • 原文地址:https://blog.csdn.net/weixin_37862824/article/details/133084264