Spring中@import注解终极揭秘!

Spring中@import注解终极揭秘 - 程序员古德

技术概念

它能干啥

@Import注解在Spring框架中主要用于解决模块化和配置管理方面的技术问题,它可以帮助开发者实现以下几个目标:

  1. 模块化配置:在大型项目中,通常需要将配置信息分散到多个配置类中,以便更好地组织和管理,@Import注解允许开发者在一个主配置类中导入其他配置类,从而实现配置的模块化。
  2. 第三方库或组件的集成:当项目中需要集成第三方库或组件时,这些库或组件可能会提供自己的配置类,通过@Import注解,开发者可以轻松地将这些第三方配置类集成到项目的总体配置中。
  3. 条件化配置@Import还可以与条件注解(如@Conditional)结合使用,以实现基于特定条件的配置加载,因此在不同的环境或情境下,可以加载不同的配置类,从而实现更加灵活和动态的配置管理。
  4. 扩展Spring功能:通过导入实现了特定接口的类,开发者可以扩展Spring框架的功能,比如,可以导入自定义的BeanFactoryPostProcessorBeanDefinitionRegistrar来修改或添加bean定义。
  5. 解决循环依赖问题:在某些情况下,使用@Import注解可以解决因循环依赖而导致的配置问题,通过将相互依赖的配置类分解并使用@Import进行导入,可以打破循环依赖的链条。

它有哪些特性

在Spring框架中,@Import注解可以用来引入一个或多个组件,这些组件通常是通过@Bean注解定义的,当使用@Import注解时,实际上是在告诉Spring:“除了当前配置类中的bean定义外,还想包含另一个配置类(或多个配置类)中定义的bean。”

@Import注解可以用来引入:

  1. 带有@Bean方法的配置类:这是最常见的情况,可以在一个配置类中定义bean,并使用@Import将其引入到其他配置类中。
  2. ImportSelector实现:这是一个更高级的特性,允许根据某些条件或运行时环境动态地选择要导入的配置类。
  3. ImportBeanDefinitionRegistrar实现:这是一个更底层的机制,允许在运行时手动注册bean定义。
  4. 使用@Import来组合多个配置类,从而构建复杂的配置层次结构。

使用@Import注解引入一个类

下面是一个简单的Java代码示例,演示了如何使用@Import注解来导入一个配置类。

首先,定义一个简单的服务接口GreetingService和其实现类GreetingServiceImpl

// GreetingService.java  
public interface GreetingService {  String sayGreeting();  
}  // GreetingServiceImpl.java  
public class GreetingServiceImpl implements GreetingService {  @Override  public String sayGreeting() {  return "Hello, World!";  }  
}

接着,创建一个配置类GreetingConfig,该类使用@Bean注解来定义GreetingService的bean:

// GreetingConfig.java  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  @Configuration  
public class GreetingConfig {  @Bean  public GreetingService greetingService() {  return new GreetingServiceImpl();  }  
}

接着,创建一个主配置类AppConfig,并使用@Import注解来导入GreetingConfig类:

// AppConfig.java  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.Import;  @Configuration  
@Import(GreetingConfig.class) // 导入GreetingConfig配置类  
public class AppConfig {  // 其他bean定义可以在这里添加  
}

最后,编写客户端代码来使用这个bean:

// Application.java  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  public class Application {  public static void main(String[] args) {  // 创建应用上下文,指定主配置类  ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);  // 从应用上下文中获取GreetingService bean  GreetingService greetingService = context.getBean(GreetingService.class);  // 调用bean的方法并打印结果  System.out.println(greetingService.sayGreeting());  // 关闭应用上下文(虽然在这个简单示例中不是必需的)  ((AnnotationConfigApplicationContext) context).close();  }  
}

运行上面代码,将会有如下输出:

Hello, World!

和ImportBeanDefinitionRegistrar接口一起使用

ImportBeanDefinitionRegistrar是一个Spring接口,它允许在运行时以编程方式注册额外的bean定义,当需要在Spring容器刷新过程中动态添加bean定义时,可以实现这个接口,ImportBeanDefinitionRegistrar通常与@Import注解结合使用,以便在Spring容器启动时执行自定义的bean注册逻辑。

下面是一个简单的案例,演示了如何使用ImportBeanDefinitionRegistrar来动态注册bean定义。

首先,创建一个简单的服务类MyDynamicService

public class MyDynamicService {  public void performTask() {  System.out.println("MyDynamicService is performing a task.");  }  
}

然后,创建一个实现了 ImportBeanDefinitionRegistrar 接口的类 MyBeanDefinitionRegistrar

import org.springframework.beans.factory.support.BeanDefinitionRegistry;  
import org.springframework.beans.factory.support.GenericBeanDefinition;  
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;  
import org.springframework.core.type.AnnotationMetadata;  public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {  @Override  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {  // 创建 GenericBeanDefinition 实例  GenericBeanDefinition beanDefinition = new GenericBeanDefinition();  // 设置 bean 类  beanDefinition.setBeanClassName(MyDynamicService.class.getName());  // 注册 bean 定义到容器中,指定 bean 的名称  registry.registerBeanDefinition("myDynamicService", beanDefinition);  }  
}

记着,需要一个配置类来触发 MyBeanDefinitionRegistrar 的注册:

import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.Import;  @Configuration  
@Import(MyBeanDefinitionRegistrar.class)  
public class MyAppConfig {  // 其他配置...  
}

最后,在应用程序中使用 AnnotationConfigApplicationContext 来加载 MyAppConfig 并获取 MyDynamicService 的实例:

import org.springframework.context.ApplicationContext;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  public class Application {  public static void main(String[] args) {  ApplicationContext context = new AnnotationConfigApplicationContext(MyAppConfig.class);  MyDynamicService myDynamicService = context.getBean(MyDynamicService.class);  myDynamicService.performTask(); // 输出:"MyDynamicService is performing a task."  }  
}

在这个例子中,当Spring容器启动时,它会处理@Import注解并调用MyBeanDefinitionRegistrarregisterBeanDefinitions方法。

这个方法会在容器中动态地注册MyDynamicService的bean定义,随后,可以像获取其他Springbean一样获取并使用MyDynamicService的实例。

和ImportSelector接口一起使用

ImportSelector是Spring框架提供的一个接口,它允许开发者在运行时根据某些条件或逻辑选择要导入的配置类,ImportSelector接口定义了一个方法selectImports,该方法返回一个字符串数组,表示要导入的配置类的全限定名。

以下是一个简单的示例,展示了如何使用ImportSelector来动态选择要导入的配置类:

首先,定义两个简单的配置类ConfigAConfigB,每个配置类都有一个Bean定义:

// ConfigA.java  
@Configuration  
public class ConfigA {  @Bean  public String configABean() {  return "Bean from ConfigA";  }  
}  // ConfigB.java  
@Configuration  
public class ConfigB {  @Bean  public String configBBean() {  return "Bean from ConfigB";  }  
}

接下来,创建一个实现 ImportSelector 接口的类 MyImportSelector,它根据某个条件(例如系统属性)来决定导入哪个配置类:

// MyImportSelector.java  
import org.springframework.context.annotation.ImportSelector;  
import org.springframework.core.type.AnnotationMetadata;  import java.util.Arrays;  public class MyImportSelector implements ImportSelector {  @Override  public String[] selectImports(AnnotationMetadata importingClassMetadata) {  // 根据某个条件决定导入哪个配置类  if (System.getProperty("config.selector") != null && "configA".equals(System.getProperty("config.selector"))) {  return new String[]{ConfigA.class.getName()};  } else {  return new String[]{ConfigB.class.getName()};  }  }  
}

在上面的 MyImportSelector 类中,selectImports 方法检查系统属性 config.selector 的值,并根据该值返回相应的配置类全限定名。

最后,创建一个主配置类 MainConfig,并使用 @Import 注解引入 MyImportSelector

// MainConfig.java  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.Import;  @Configuration  
@Import(MyImportSelector.class)  
public class MainConfig {  // 其他配置...  
}

现在,在应用程序中,可以根据系统属性 config.selector 的值来动态选择要加载的配置类:

// Application.java  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  public class Application {  public static void main(String[] args) {  // 设置系统属性以决定要导入的配置类  System.setProperty("config.selector", "configA"); // 设置为 "configA" 或 "configB"  ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);  // 根据系统属性的设置,以下将打印出不同的结果  String bean = context.getBean(String.class);  System.out.println(bean);  }  
}

运行上述 Application 类的 main 方法,并根据需要设置系统属性 config.selector 的值,将看到根据该值动态加载了不同的配置类中的 Bean。如果设置为 “configA”,则输出将是 “Bean from ConfigA”;如果设置为其他值或未设置,则输出将是 “Bean from ConfigB”。

使用@Import注解引入多个类

当使用@Import注解来组合多个配置类时,可以在一个主配置类上使用@Import注解,并指定要导入的其他配置类的类名。这样做可以让Spring框架在创建应用上下文时,加载并处理这些配置类中定义的bean。

以下是一个简单的代码案例,展示了如何使用@Import来组合多个配置类:

首先,定义两个简单的配置类,每个配置类都使用@Bean注解来定义一个不同的bean:

// FirstConfig.java  
@Configuration  
public class FirstConfig {  @Bean  public String firstBean() {  return "First Bean";  }  
}  // SecondConfig.java  
@Configuration  
public class SecondConfig {  @Bean  public String secondBean() {  return "Second Bean";  }  
}

记着,创建一个主配置类,并使用@Import注解来导入上面定义的两个配置类:

// MainConfig.java  
@Configuration  
@Import({FirstConfig.class, SecondConfig.class})  
public class MainConfig {  // 这里不需要定义任何bean,因为只是组合其他配置类  
}

最后,编写一个客户端类来演示如何从应用上下文中获取这些bean:

// Application.java  
public class Application {  public static void main(String[] args) {  // 创建应用上下文,并指定主配置类  ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);  // 从应用上下文中获取FirstConfig中定义的bean  String firstBean = context.getBean("firstBean", String.class);  System.out.println("First Bean: " + firstBean);  // 从应用上下文中获取SecondConfig中定义的bean  String secondBean = context.getBean("secondBean", String.class);  System.out.println("Second Bean: " + secondBean);  // 关闭应用上下文(虽然不是必需的,但在实际应用程序中应该这样做)  ((ConfigurableApplicationContext) context).close();  }  
}

当运行Application类的main方法时,输出将会是:

First Bean: First Bean  
Second Bean: Second Bean

这证明了Spring成功地加载了FirstConfigSecondConfig中定义的bean,并将它们组合到了由MainConfig类创建的应用上下文中,通过这种方式,可以轻松地将多个配置类组合在一起,从而构建更复杂的配置层次结构。

关注我,每天学习互联网编程技术 - 程序员古德

END!
END!
END!

往期回顾

精品文章

Java并发基础:concurrent Flow API全面解析

Java并发基础:CopyOnWriteArraySet全面解析

Java并发基础:ConcurrentSkipListMap全面解析

Java并发基础:ConcurrentSkipListSet全面解析!

Java并发基础:SynchronousQueue全面解析!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/722466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA-DDS原理及实现

DDS(Direct Digital Synthesizer)即数字合成器,是一种新型的频率合成技术,具有相对带宽大,频率转换时间短、分辨率高和相位连续性好等优点。较容易实现频率、相位以及幅度的数控调制,广泛应用于通信领域。 相位累加器是由N位加法器与N位寄存器构成,每个时钟周期的上升沿,加法器…

数据中心制冷系统设计与发展

数据中心概要与传统建筑空间相比,数据中心散热密度大,单位面积散热量可达传统办公区域的40倍以上,且越来越呈现集中化、大型化的趋势;同时,设备的安全性需求提高了对内部空调温湿度和洁净度的要求,数据中心…

【Qt】Qwidget的常见属性

目录 一、Qwidget核心属性 二、enable属性 三、geometry属性 四、 WindowFrame的影响 五、windowTitle属性 六、windowIcon属性 七、qrc文件管理资源 八、windowOpacity属性 九、cursor属性 十、font属性 十一、toolTip属性 十二、focusPolicy属性 十三、styleShe…

STM32FreeRTOS-事件组1(STM32Cube高效开发教程)

文章目录 一、事件组的原理和功能1、事件组与队列信号量特点2、事件组存储结构3、事件组运行原理 二、事件组部分函数1、xEventGroupCreate()创建事件组函数2、xEventGroupSetBits()事件组置位函数3、xEventGroupSetBitsFromISR()…

Sychronized和ReentrantLock锁 面试题

Sychronized和ReentrantLock锁 面试题 前言1、Java死锁如何避免?2、公平锁和⾮公平锁的底层实现?3、ReentrantLock中tryLock()和lock()⽅法的区别?4、Sychronized的偏向锁、轻量级锁、重量级锁?5、谈谈你对AQS的理解,A…

SQL技巧笔记(一):连续3人的连号问题—— LeetCode601.体育馆的人流量

SQL 技巧笔记 前言:我发现大数据招聘岗位上的应聘流程都是需要先进行笔试,其中占比很大的部分是SQL题目,经过一段时间的学习之后,今天开了一个力扣年会员,我觉得我很有必要去多练习笔试题目,这些题目是有技…

代码随想录算法训练营第三十七天 | LeeCode 738. 单调递增的数字

题目链接:738. 单调递增的数字 - 力扣(LeetCode) class Solution { public:int monotoneIncreasingDigits(int N) {string strNum to_string(N);// flag用来标记赋值9从哪里开始// 设置为这个默认值,为了防止第二个for循环在fla…

Linux - 进程概念

1、冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系; 截至目前,我们所认识的计算机,都是有一个个的硬件组件组成: 输入单元:…

【JavaEE】_Spring MVC项目使用数组与集合传参

目录 1. 使用数组传参 1.2 传递单个参数 1.3 传递多个名称相同的参数 1.3.1 关于urlencode 2. 使用集合传参 1. 使用数组传参 创建一个Spring MVC项目,其中 .java文件内容如下: package com.example.demo.controller;import com.example.demo.Per…

2.Zookeeper集成springboot操作节点,事件监听,分布式锁实现

1.Springboot项目中添加zookeeper 已经对应的客户端依赖 &#xff0c;pom.xml文件如下 <!-- Zookeeper组件 --><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.9.1</version…

【C++】6-8 评委打分 分数 10

6-8 评委打分 分数 10 全屏浏览 切换布局 作者 刘利 单位 惠州学院 某诗歌朗诵比赛&#xff0c;有n位评委给参赛者打分&#xff0c;计算总分时要去除最高分和对低分。 要求&#xff1a;编写名为cmax和cmin的函数分别返回最高分的和最低分元素的引用&#xff0c;带有2个形参…

leetcode面试经典算法题——1

链接&#xff1a;https://leetcode.cn/studyplan/top-interview-150/ 392. 判断子序列 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串…

292.【华为OD机试】跳马问题(广度优先搜索(BFS)JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目二.解题思路三.题解代码Python题解代码JAVA题解…

分布式事务(SeataServer)

SeataServer搭建 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Servi…

JavaScript 闭包 作用域

闭包 JavaScript 中的闭包是相当重要的概念并且与作用域相关知识的指向密切相关 JavaScript 中的作用域是什么意思?闭包会在哪些场景中使用?通过定时器循环输出自增的数字通过 JS 的代码如何实现? 闭包概念一 闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包…

自我对比: 通过不一致的解决视角更好地进行反思

一、写作动机&#xff1a; LLM 在自我评价时往往过于自信或随意性较大&#xff0c;提供的反馈固执或不一致&#xff0c;从而导致反思效果不佳。为了解决这个问题&#xff0c;作者提倡 "自我对比"&#xff1a; 它可以根据要求探索不同的解决角度&#xff0c;对比差异…

ChatGPT如何辅助医生改善AD患者教育的效果

特应性皮炎&#xff08;AD&#xff09;是一种常见的慢性炎症性皮肤病&#xff0c;在全球范围内造成了巨大的疾病负担。尽管在治疗方面取得了一定进展&#xff0c;但AD患者的生活质量较低&#xff0c;治疗满意度差&#xff0c;超过一半的患者认为中度至重度AD疾病控制不佳。AD的…

YOLO快速入门

Yolo简介 概述 YOLO&#xff08;You Only Look Once&#xff09;是一种流行的目标检测算法&#xff0c;由Joseph Redmon等人开发。 YOLO算法以其高效的实时性能和准确的检测能力而闻名。自YOLO的首次提出以来&#xff0c;已经经 历了多个版本的更新和改进。以下是YOLO发展史的…

周边类-找厕所小程序源码

源码获取方式 1&#xff0c;搜一搜 万能工具箱合集 点击资料库 即可进去获取 找厕所小程序源码依赖于腾讯地图的一款源码&#xff0c;腾讯地图api免费申请&#xff0c;是一款免费又永久的不需要服务器的小程序&#xff0c;起个好名字蹭蹭蹭~ 搭建教程&#xff1a; 1、下载源码…

使用css的transition属性实现抽屉功能

需求 使用css手写一个抽屉&#xff0c;并且不能遮挡住原来的页面 效果&#xff1a;&#xff08;录的gif有点卡&#xff0c;实际情况很丝滑&#xff09; 实现代码&#xff1a; <template><div class"dashboard-container"><div class"mainBox&…