Spring中@import注解终极揭秘!

news2024/11/20 9:34:35

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.coloradmin.cn/o/1491626.html

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

相关文章

Stable Diffusion ———LDM、SD 1.0, 1.5, 2.0、SDXL、SDXL-Turbo等版本之间关系现原理详解

前言 2021年5月,OpenAI发表了《扩散模型超越GANs》的文章,标志着扩散模型(Diffusion Models,DM)在图像生成领域开始超越传统的GAN模型,进一步推动了DM的应用。 然而,早期的DM直接作用于像素空…

羊大师讲解如何判断自己适不适合喝羊奶

羊大师讲解如何判断自己适不适合喝羊奶 判断自己是否适合喝羊奶,可以考虑以下几个方面: 身体健康状况:如果身体健康,没有特殊的疾病或过敏史,那么一般来说,喝羊奶是没有问题的。然而,如果有某…

Django框架——请求与响应

上篇文章我们学习了Django框架——配置文件和视图函数,这篇文章我们学习Django框架——请求与响应。 客户端和服务端的请求与响应过程:客户端访问某个网站并发出URL请求,服务器接受到请求后,根据请求内容来返回响应,如…

掌握X-Content-Type-Options头的防护之力

欢迎来到我的博客,代码的世界里,每一行都是一个故事 掌握X-Content-Type-Options头的防护之力 前言X-Content-Type-Options头的作用未定义X-Content-Type-Options的风险如何配置X-Content-Type-Options头常见MIME类型不匹配的问题 前言 你是否曾遇到过浏…

马士超:符合国际标准的沉浸式音频HOLOSOUND的发展与未来 | 演讲嘉宾公布

一、3D音频 3D 音频分论坛将于3月27日同期举办! 3D音频技术不仅能够提供更加真实、沉浸的虚拟世界体验,跨越时空的限制,探索未知的世界。同时,提供更加丰富、立体的情感表达和交流方式,让人类能够更加深入地理解彼此&a…

TensorRT入门:trtexec开发辅助工具的使用

文章目录 一、trtexec简介二、trtexec使用1.trtexec常用参数1. 构建阶段2. 运行阶段 2.基本使用方法1. trtexec最基本的使用方法,读取onnx模型并通过trtexec测试推理性能。2. trtexec解析ONNX文件,使用优化选择构建TensorRT引擎并保存至.plan文件补充&am…

C++ string类详解及模拟实现

目录 【本节目标】 1. 为什么学习string类? 1.1 C语言中的字符串 1.2 面试题(暂不做讲解) 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明(注意下面我只讲解最常用的接口) 3. string类的模拟实现 3.1string类常用…

OpenAI 3年前的AI音乐生成项目:Jukebox,效果比SunoAI v3还好

原来OpenAI 3年前就开始搞AI音乐生成了 效果甚至比最近发布的sunoAI v3还要好,难道OpenAI 想把这个隐藏大招练成无人能敌的状态才放出来再一次轰动全球? OpenAI在2019年8月份就推出了他们的一音乐生成模型:Jukebox Jukebox能够根据提供的歌…

【快速上手QT】07-对话框QDialog

QDialog 今天讲一个我们这个系列的第一篇就提到的东西&#xff1a;QDialog。 相信经过前几篇的学习&#xff0c;大家应该是能够通过QT助手来对QDialog有个初步的了解。 我们就直接来测试一下。 #include "Zhetu.h"#include <qdebug.h> #include <QPushBu…

C++输入输出(I\O)

我们知道C是由C语言发展而来的&#xff0c;几乎完全兼容C语言&#xff0c;换句话说&#xff0c;你可以在C里面编译C语言代码。如下图: C语言是面向过程的语言&#xff0c;C在C语言之上增加了面向对象以及泛型编程机制&#xff0c;因此C更适合中大型程序的开发&#xff0c;然而C…

RK DVP NVP6158配置 学习

NVP6158简介 NVP6158C是一款4通道通用RX&#xff0c;提供高质量图像的芯片。它接受来自摄像机和其他视频信号的独立4通道通用输入来源。它将4通道通用1M至8M 7.5P视频格式数字化并解码为代表8位ITU-R BT.656/1120 4:2:2格式的数字分量视频&#xff0c;并将单独的BT.601格式与27…

计算机网络——概述

计算机网络——概述 计算机网络的定义互连网&#xff08;internet&#xff09;互联网&#xff08;Internet&#xff09;互联网基础结构发展的三个阶段第一个阶段——APPANET第二阶段——商业化和三级架构第三阶段——全球范围多层次的ISP结构 ISP的作用终端互联网的组成边缘部分…

Nodejs 第四十九章(lua)

lua Lua是一种轻量级、高效、可嵌入的脚本语言&#xff0c;最初由巴西里约热内卢天主教大学&#xff08;Pontifical Catholic University of Rio de Janeiro&#xff09;的一个小团队开发而成。它的名字"Lua"在葡萄牙语中意为"月亮"&#xff0c;寓意着Lua…

数据分析-Pandas数据y轴双坐标设置

数据分析-Pandas数据y轴双坐标设置 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&…

平衡搜索二叉树—AVL树

一、定义&#xff1a; 为了避免搜索二叉树的高度增长过快&#xff0c;降低二叉树的性能&#xff0c;规定在插入和删除二叉树的结点的时候&#xff0c;任何结点左右子树的高度差绝对值不超过1&#xff0c;这样的二叉树被称为平衡二叉树&#xff08;balanced Binary Tree&#xf…

为PDF创建目录(侧边栏目录)

通过可以新建书签的pdf阅读器。 知云翻译&#xff1a;可以新建书签和子书签。 Adobe Acrobat&#xff1a;只能新建书签&#xff0c;不能建立子书签。

DA14531在三星手机手写笔的应用让我打开眼镜

手写笔的功能 这是一款内置蓝牙功能的魔性笔&#xff0c;它是遥控器、是照相、切换摄像头、是暂停或者打开播放列表。乃至更多操作-通过不同的手势隔空操作&#xff0c;或者按下触控按键便可轻松搞定。 手写笔硬件设计 内部结构 采用2.3V可循环充电电池&#xff0c;放入手…

软件测试零基础新手入门必看

软件测试&#xff1a;使用技术手段验证软件是否满足使用需求 目的&#xff1a;减少缺陷&#xff0c;保证质量 一、测试主流技能&#xff1a; 1.功能测试 测试主要验证程序的功能是否满足需求 2.自动化测试 使用工具或代码代替手工&#xff0c;对项目进行测试 3.接口测试 …

【原理图PCB专题】Allegro模块化移动器件报...has the LOCKED property怎么解锁?

在模块化原理图时,PCB也需要做一个模块.mdd文件。这时需要先画好图纸然后再制作模块化文件。 修改文件时会发现模块化器件报错,无法编辑模块内部器件和走线,器件和走线都被LOCKED,如下所示报错内容: Symbol "U1" Selected Cannot edit Symbol "U1". M…

(Linux学习七)进程介绍

一、进程 进程生命周期&#xff1a;由系统程序。form出来的子程序&#xff0c;具备一定的父的资源&#xff08;权利&#xff0c;内存空间&#xff0c;PID&#xff09;直到运行完毕&#xff0c;退出系统 查看进程 ps aux 查看所有进程参数&#xff1a;aux ps a 显示现行…