Spring框架的ImportSelector到底可以干嘛

Spring - ImportSelector 介绍 要使用ImportSelector接口,需要创建一个实现该接口的类,并将该类添加到@Configuration注解中的@Import注解的value属性中。Spring容器在解析@Configuration注解时,会自动调用该类的selectImports()方法,并根据返回的配置类全限定名来导入相应的配置类。可以根据自定义的逻辑来实现该方法,根据条件选择要导入的配置类。使用ImportSelector接口可以实现更加灵活的配置类导入方式,可以根据不同的条件选择不同的配置类,从而满足不同的需求。 阅读详情
AI摘要

本文深入探讨Spring框架中ImportSelector接口的使用与作用,通过实例演示其如何动态控制类的加载,实现功能模块的灵活开关。同时,对比分析ImportSelector与DeferredImportSelector的区别。

前言

最近一直在钻研Spring源码,感觉要把自己看吐了,但是看到奥妙的地方还是会拍手称快。这次就总结一下Spring中一个非常重要的注解@Import中的ImportSelector接口的作用以及它到底有啥作用。也会捎带一部分源码说一下DeferredImportSelector是干啥的,以及Spring解析这个和ImportSelector有什么区别。更多Spring内容进入【Spring解读系列目录】。

ImportSelector

说到ImportSelector这个接口就不得不说这里面最重要的一个方法:selectImports()。

public interface ImportSelector {

   /**
    * Select and return the names of which class(es) should be imported based on
    * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
    * 选择并返回需要导入的类的名称,这些类基于AnnotationMetadata
    * 并且导入到@Configuration注解的类中的
    * @return the class names, or an empty array if none
    * 返回所有的class name,如果没有,就返回空
    */
   String[] selectImports(AnnotationMetadata importingClassMetadata);

   /**
    * 返回排除的类,是一个类过滤器,但是这个方法被default注解了,
    * 可见Spring公司也知道,这个基本没啥人用
    */
   @Nullable
   default Predicate<String> getExclusionFilter() {
      return null;
   }

}

源码的注解里说了一大堆,就直接说这个方法能干啥吧。这个方法的返回值是一个字符串数组,只要在配置类被引用了,这里返回的字符串数组中的类名就会被Spring容器new出来,然后再把这些对象放到工厂当中去。所以这有啥用呢?我们还是用一个例子演示一下。

ImportSelector简单例子

首先我们先有一个实现了ImportSelector的类MyImportSelect,再构造一个业务类IndexDao,然后配置类用@Import引入,最后测试类Test。

/**
 * 由于我们使用的ImportSelector所以就不需要放到Spring容器当中了。
 * 我们要用@Import这个注解引入进去。
 */
public class MyImportSelect implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{IndexDao.class.getName()};
    }
}
@ComponentScan("com.demo")
@Import(MyImportSelect.class)
public class AppConfig {
}
public class IndexDao {
    public void query(){
        System.out.println("query IndexDao for MyImportSelect");
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);
         anno.getBean(IndexDao.class).query();
         }
}
运行打印
query IndexDao for MyImportSelect

从上面的例子看,尽管程序上没有把MyImportSelect类放到Spring容器中,也没有把IndexDao放到Spring容器中,但是在测试上就可以把IndexDao从容器中拿出来,并且正常执行。

不知道大家看到这里有什么感觉,到这里我其实是有疑问的。我这么做有个卵用噻。我是有病吧,直接把类上加个@Component注册进去不香吗?所以这个ImportSelector把程序搞这么复杂是有毛病吧,把简单的功能搞这么复杂。
在这里插入图片描述

ImportSelector真正的作用

其实Spring公司既然这么设计,那肯定是有用的。那么有什么用呢?设想这样一个场景,如果有些功能我们并不需要Spring在一开始就加载进去,而是需要Spring帮助我们把这些功能动态加载进去,这时候这个ImportSelector的作用就来了。我们完全可以把实现这个接口的类做成一个开关,用来开启或者关闭某一个或者某些功能类。

比如说我们上面的例子IndexDao,假设这个IndexDao实现的功能是一个扩展功能,在正式的生产上不一定用的到。如果说一个包下有100个类,那么使用扫描去屏蔽这个类就很麻烦,但是屏蔽这个类用ImportSelector去做就很容易了。下次如果需要用到了,我再放开这个开关,直接可以使用IndexDao的功能了,这样就做到了一个灵活的功能掌控。

再举一个实际的用例,假设我们的IndexDao不是打印而是返回一个针对代理IndexDao3的代理对象,比如输出一个log。但是这个方法我不一定会用到,因为只有需要用到代理的时候,这个方法才有意义。我不需要去代理,这里的代码就不要运行。只有给一个显示的通知,我这个代理才会去执行。

public class IndexDao {
    public void query(){
	System.out.println("log for IndexDao3");
        return Proxy.newProxyInstance(IndexDao3);//伪码
    }
}

看到这里,大家有没有联想到SpringAOP其实就是这个样子。能够做到动态加载与卸载,与我们的程序没有什么耦合关系。怎么才能实现这个所谓的动态开启呢?

ImportSelector开关

为了完成这个开关,我们也模仿Spring写一个EnableMySelector的注解,然后@Import我们自己的ImportSelector接口类。

@Retention(RetentionPolicy.RUNTIME) //开启运行时加载
@Import(MyImportSelect.class)
public @interface EnableMySelector {
}

做好了这一步在AppConfig这个配置里面就可以直接使用这个@ EnableMySelector自定义注解去开关一个类了。

@ComponentScan("com.demo")
@EnableMySelector
public class AppConfig { }
运行,一样打印
query IndexDao for MyImportSelect

讲道理其实Spring中那么多的EnableXXXX的注解底层就是这样的原理。到此还有谁敢说ImportSelector用处小?
在这里插入图片描述

连带说一下DeferredImportSelector

这个是看Spring源码的时候发现的,直接翻译就是延时加载ImportSelector,实现这个接口的类,将会在@Configuration后面被加载,用法什么的和ImportSelector功能基本一样。因为用的比较稀有就不多做解释了,仅仅作为一个只是扩展点介绍下。在ConfigurationClassParser中会有一个判断,是不是这个接口,如果是就会放到后面解析。以下摘自源码:

org.springframework.context.annotation.ConfigurationClassParser#processImports
//这里拦截了DeferredImportSelector然后使用handle()
if (selector instanceof DeferredImportSelector) {
   this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}

进入handle()方法,发现和这个接口相关的都被加入了一个deferredImportSelectors的list中。

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
   DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
   if (this.deferredImportSelectors == null) {
      DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
      handler.register(holder);
      handler.processGroupImports();
   }
   else {
	//加入到了一个ArrayList中
      this.deferredImportSelectors.add(holder);
   }
}

最终这个ArrayList在parse()方法的最后被处理了

org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   //根据BeanDefinition的类型做不同的处理,一般都会调用ConfigurationClassParser.parse()进行解析
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition(); //拿出BeanDefinition
      try {
         if (bd instanceof AnnotatedBeanDefinition) {  //判断是不是加了注解的
            // 解析注解对象,并且把解析出来的bd方法map中,但是这里的bd指的的普通的
            // 普通和不普通的怎么区分。比如@Bean和各种beanFactoryPostProcessor得到的bean
            //如果被加了注解,又调用了一个parse()方法
            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());
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
   }
	//处理,而此时上面其他的Import已经处理完了
   this.deferredImportSelectorHandler.process();
}

总结

不得不说Spring公司的大神们编程思想是真的厉害,但是这代码和注释写的是真的难以下咽。那么下一篇【Spring框架中ImportBeanDefinitionRegistrar和ImportSelector的区别】我们就说下另一个重要的@Import接口: ImportBeanDefinitionRegistrar。这个接口更加的厉害,它能够干预BeanDefinition注册过程,而且我们经常使用的大名鼎鼎的Mybatis就是根据这个接口和Spring框架进行的整合。最后感谢各位阅读到此。

Spring Boot自动装配原理及实践 微服务项目中,基于日志排查问题是非常重要的手段,而日志属于非功能范畴的一个职责,所以我们希望将日志打印和功能解耦。AOP就是非常不错的手段,但是在每个服务中都编写一个切面显然是非常不可取的。所以我们希望通过某种手段会编写一个通用日志打印工具,只需一个注解即可实现对方法的请求响应进行日志打印。所以我们这个例子仍然是利用自动装配原理编写一个通用日志组件。 阅读详情

相关推荐

Spring系列——@Import、ImportSelector接口

Spring注解之@Import前言@Import源码 前言 我们已经知道两种方式给Spring注册组件 包扫描+注解的方式(@Controller、@Service、@Reposity、@Comment) 使用@Configuration+@Bean的方式 这两种的使用区别,第一种只能注册我们自己写的组件,而第二种可以注册jar包里面的组件 接下来我来介绍第三种方式使用@Import来给Spring注册组件 @Import源码 @Target(ElementType.TYPE) @Retention(

明亮的星光 1248

Spring(32)——ImportSelector介绍

ImportSelector介绍 在@Configuration标注的Class上可以使用@Import引入其它的配置类,其实它还可以引入org.springframework.context.annotation.ImportSelector实现类。ImportSelector接口只定义了一个selectImports(),用于指定需要注册为bean的Class名称。当在@Configurati...

Elim的博客 1万+

深入理解Spring ImportSelector与DeferredImportSelector

通过对比和的使用,我们可以看到它们在控制配置类导入顺序上的差异。在配置类处理前导入,而在所有其他配置类处理完毕后导入。这为我们在复杂的Spring应用中提供了更多的灵活性和控制力。

m0_62153576的博客 718

深入理解Spring的ImportSelector接口

在这里我们仿照@EnableTransactionManagement来实现自定义注解,注意使用@Import导入我们刚才写的SpringStudySelector。

CHIKA2333的博客 1323

Spring IOC之ImportSelector接口

ImportSelector是一个强大的机制,它使你能够根据运行时的条件来动态配置Spring容器,这在某些复杂的应用场景中非常有用。你可以编写自定义的ImportSelector实现来根据自己的需求选择要导入的配置类。

qq_57756904的博客 620

抖音短视频数据抓取实战系列(一)——模拟器性能对比与优化配置

本文是抖音短视频数据抓取实战系列的开篇,重点探讨了模拟器选型与优化配置。通过对比雷电、夜神、逍遥三大主流模拟器在性能、抖音兼容性及抓包友好度上的表现,文章指出雷电模拟器因其稳定的桥接网络模式和良好的兼容性,是数据抓取的优选。并提供了雷电模拟器从安装、网络桥接到系统优化的详细配置指南,旨在帮助读者构建稳定高效的抓取环境。

weixin_29230901的博客 248

spring中的ImportSelector接口详解

Spring框架的ImportSelector接口是实现动态配置导入的核心机制,广泛应用于模块化条件配置。该接口通过selectImports方法解析注解元数据,返回需导入的配置类全名数组。支持与Aware接口集成获取容器信息,其子接口DeferredImportSelector可实现延迟加载。典型应用场景包括环境适配配置、功能模块按需加载和第三方框架集成。示例展示了如何根据企业客户标记动态切换数据源配置,通过@Import注解触发选择器逻辑,体现了Spring灵活的可扩展性设计。该机制是SpringBoo

技术人集结地 1598

NetApp 7-mode初始化详解(原AGGR重建)及注意事项

没想到有生之年还能再写个7-mode的文章,上次接触可能已将近十年,本次恰巧碰到这个机会,边学习边整理,整整一天时间,碰到了不少坑,作为记录。

RaySun的技术Blog 2752

Spring中ImportSelector接口的作用

Spring中ImportSelector接口的作用 @Import()注解的作用: 当我们需要导入某个类到spring容器中去,但spring恰好无法扫描到这个类,而我们又无法修改这个类 (jar包形式)。我们就可以通过@import(xxx.class)是将这个类导入到spring容器中。 写一个例子; 创建一个Test类: //不加任何注解或配置bean.xml,仅仅表示一个外部类,与spr...

Leehom_Wang的博客 1466

在Keil下完成一个汇编程序的编写

在学习STM32的路上开始是学的C语言尚未进行对汇编进行学习,因此抽出时间来学习一下在keil下的汇编语言。

Liu-FE的博客 1万+

用yolov8训练自己的模型并调取摄像头实时检测,新手小白超友好教程!逻辑梳理,从0开始逐步实操❤

帮新手小白学会用yolov8,包含多篇高质量博客整理

Lhhhhds的博客 2941

Spring框架的ImportSelector使用场景

Spring框架的ImportSelector使用场景 说起ImportSelector接口,不得不说@Import注解,该注解的作用是给容器中导入一个或多个组件,看下注释 public @interface Import { /** * {@link Configuration @Configuration}, {@link ImportSelector}, * {@link ImportBeanDefinitionRegistrar}, or regular componen

songbing_的博客 1055

Android Framework WMS全面概述和知识要点

WindowManagerService 类是整个 WMS 的核心大脑,处于系统服务层,负责协调和管理所有与窗口相关的操作。它在 Android 系统架构中起到承上启下的关键作用,向上对接应用层的窗口请求,向下与 SurfaceFlinger 等底层组件交互,确保窗口能够正确地在屏幕上显示和管理。

大模型大数据攻城狮的专栏 1795

Spring底层原理之bean的加载方式六 实现ImportSelector接口

重写方法 后直接把要加载的bean放到字符串数组里返回。这个方法可以做一些动态的bean的加载。接口为 ImportSelector。之后拿到配置类6里面的所有bean。这种方式在实际开发中不常见。那为什么我们要折腾一大圈呢。涉及到一个概念 叫 元注解。作为bean加载进配置类。但是在框架内部大量使用。这样加载后就能创建对象。我们这个对象有很多方法。是用接口的方式实现的。我们写一个Main类。在框架底层是填充作用。

多多的博客 11万+

【29】软考软件设计师——SQL语句编写与优化深度精讲|数据库大题延伸满分攻略

本文是《软件设计师·50讲通关|从零基础到工程师职称》专栏第29篇,承接第28篇ER图转关系模式核心内容,作为**下午第2题数据库大题核心延伸必考模块**,单模块累计占分5~8分,是数据库板块性价比极高的提分重点。全文超4000字深度拆解软考全部SQL高频考点:全覆盖多表连接底层逻辑、嵌套子查询分类实操、GROUP BY分组聚合硬核规则、索引全品类设计原理、联合索引最左匹配底层机制,搭配海量可直接复刻的考场SQL模板、历年真题完整改写案例、索引失效场景实操复盘、慢查询优化落地技巧。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 526

深度剖析OpenHarmony AI Engine:开发板端侧大模型推理插件机制全链路拆解

/ 加载动态库// 获取符号地址// 关闭动态库// 获取错误信息public:// 单例获取// 销毁实例// 加载插件// 卸载插件private:// 获取插件实例// 插件映射锁// 插件映射表public:Plugin();~Plugin();// 加载算法// 卸载算法// 获取算法实例// 设置算法实例private:// 算法接口指针// 动态库句柄// 算法ID// 版本信息// 配置信息字符串int len;

m0_67505153的博客 509

【Spring】深入理解 Spring 中的 ImportSelector、Aware 和 Processor 接口

Spring 框架提供了一系列接口和机制,为开发者提供了灵活、可扩展的编程模型。其中,ImportSelector、Aware 接口以及 Processor 系列接口是非常重要的扩展点,本文将深入探讨它们的设计目的、使用方法以及示例应用。

DC1020的博客 1453

MacBook Air老本重装系统

手里的这个本子型号是A1466,cpu i5,4G内存,硬盘ssd 120G。 重装系统可以在原系统上升级到更新版本,或者用U盘安装。 先确定,这个本子可以安装什么系统。 最权威的信息当然是apple的官网,地址:https://support.apple.com/zh-cn/HT211683 在“检查兼容性”中,可以依次检查每个系统兼容的硬件型号。Mojave是A1466能安装的最高版本。 确认兼容性后,下一步就是下载安装文件。还是这个页面稍下面一点可以找到。 这里告诉用户文件可以在什

bigwood99的博客 2万+

spring注解 @Import的使用,ImportSelector接口,ImportBeanDefinitionRegistrar接口

@Import:导入bean的实例,是bean注入的一种的一种形式,默认的bean的id是全类名 @Import注解,我们先看定义; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuratio...

鸭鸭的博客 1443

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【Spring注解驱动开发】深入理解Spring的ImportSelector接口

写在前面 在前面的文章中,我们知道了可以使用ImportSelector接口实现向Spring容器中导入bean,那ImportSelector接口是如何实现的呢,接下来,我们就一探究竟! 项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation ImportSelector接口概述 ImportSelector接口是至spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都

冰河的专栏 1564

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

ImportSelector使用——spring容器bean的注入

zzzz

ww0peo的博客 1225

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean

写在前面 在上一篇关于Spring的@Import注解的文章《【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件》中,我们简单介绍了如何使用@Import注解给容器中快速导入一个组件,而我们知道,@Import注解总共包含三种使用方法,分别为:直接填class数组方式;ImportSelector方法(重点);ImportBeanDefinitionRegistrar方式。那么,今天,我们就一起来学习关于@Import注解非常重要的第二种方式:ImportSelector方式。

冰河的专栏 2182

Spring.ImportSelector接口

一、关于ImportSelector接口 ImportSelector接口是至spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在 二、ImportSelector接口源码 package org.springframework.context.annotation; import org.springframework.core.type.AnnotationMetadata; /** * Interface to be imple

cristianoxm的博客 909

Spring之ImportSelector接口浅析

@Import注解是将指定的Bean加入到IOC容器之中进行管理,ImportSelector接口只有一个selectImports方法,该方法将返回一个数组,也就是类实例名称,@Import 注解将会把返回的Bean加入到IOC容器中进行管理; 1.看下ImportSelector接口的源码 package org.springframework.context.annotation; imp...

OceanSky的专栏 3555

Spring扩展之@Import 和 ImportSelector 接口

简介 /* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * htt

指尖凉的博客 936
上一篇: Spring后置处理器BeanPostProcessor的应用
下一篇: 从山寨Spring中学习Spring 动态加载
Smallc0de
博客等级 码龄15年 148粉丝 147原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值