SpringBoot——自动装配之@Import

文章目录

  • 前言
  • @Import
    • @Import 的作用
      • 1、@Import(MyDemo1.class) 将某个对象加载至bean容器中
      • 2、@Import一个类 该类实现了ImportSelector, 重写selectImports方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中
      • 3、@Import一个类,该类实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry bd的注册器,能手工往beanDefinitionMap中注册 beanDefinition
  • SpringBoot是如何实现其他框架的热插拔的?
  • 探究 DeferredImportSelector

前言

最近在琢磨为啥spring可以很优雅、很容易地并进其他项目的原理。刚好与springboot的自动装配有点关系,特此记录下。

@Import

说起自动装配,在SpringBoot中,通常会使用一个@Enable....的注解进行加入其他项目源码。比如常见的@EnableAutoConfiguration、还有分布式中的@EnableDiscoveryClient@EnableFeign等等。

其中都有@Import的使用,说到这里,这个注解主要是做什么用的呢?

@Import 的作用

在这里插入图片描述

1、@Import(MyDemo1.class) 将某个对象加载至bean容器中

如下例子所示,创建一个bean类,其中创造一个方法。

public class MyDemo1 {

    public void test() {
        System.out.println("MyDemo1。。。。。");
    }
}

创建一个测试类,注意该测试类可以被spring扫描到。本次使用AnnotationConfigApplicationContext进行验证,如下所示:

import cn.xj.bean1.MyDemo1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(MyDemo1.class) // import 注入某个对象
public class Test1 {

    public static void main(String[] args) {
    	// 注解扫描对应的bean 
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Test1.class);
        MyDemo1 bean = applicationContext.getBean(MyDemo1.class);
        // 调用对象的方法
        bean.test();
    }

}

运行这个类,查看控制台打印效果,如下所示:
在这里插入图片描述

2、@Import一个类 该类实现了ImportSelector, 重写selectImports方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中

还是上面的那个MyDemo1 ,本次换一个方法进行bean的注入

实现 ImportSelector 并重写 selectImports

如下所示:

import cn.xj.bean1.MyDemo1;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyDemoConfig2 implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyDemo1.class.getName()};
    }
}

创建一个配置类,将这个实现 ImportSelector 并重写 selectImports的类进行载入。

import cn.xj.bean1.MyDemo1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(MyDemoConfig2.class) // 导入的是一个实现 ImportSelector 并重写 selectImports 的类
public class MyDemoConfigTest2 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = 
        		new AnnotationConfigApplicationContext(MyDemoConfigTest2.class);
        MyDemo1 bean = applicationContext.getBean(MyDemo1.class);
        bean.test();
    }
}

运行这个配置类,查看控制台日志打印,如下所示:
在这里插入图片描述

3、@Import一个类,该类实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry bd的注册器,能手工往beanDefinitionMap中注册 beanDefinition

在源码中,针对@ImportSelector的说明如下
在这里插入图片描述
依旧还是上面这个MyDemo1类,这次配置类用@ImportSelector的变种ImportBeanDefinitionRegistrar,代码如下所示:

import cn.xj.bean1.MyDemo1;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyDemoConfig3 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition bd = new RootBeanDefinition();
        bd.setBeanClass(MyDemo1.class);
        registry.registerBeanDefinition("MyDemo1", bd); // 对这个bean起一个别名
    }
}

然后再将这个类,采取@Import的方式进行载入。如下所示:

import cn.xj.bean1.MyDemo1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(MyDemoConfig3.class)
public class MyDemoConfigTest3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = 
        		new AnnotationConfigApplicationContext(MyDemoConfigTest3.class);
        MyDemo1 bean = (MyDemo1) applicationContext.getBean("MyDemo1"); // 因为是别名注入,所以采取别名获取bean
        bean.test();
    }
}

执行后,控制台输出如下日志信息:
在这里插入图片描述

SpringBoot是如何实现其他框架的热插拔的?

在spring的源码中,对于类创建<bean>实例于容器中,采取的是IOC控制反转的思想,其主要的逻辑流程如下所示:
在这里插入图片描述
spring在启动时,会加载xml配置文件config 类注解扫描import注解等方式进行bean的扫描、创建和载入容器。假设现在有一个另外的框架,springboot能够很简单的将其并入其中,并进行其他框架bean的加载项,配置项的加载项等。如:分布式微服务模块嵌入feign。

在本篇文章@Import的描述中,就说到一个东西。

@Enable....实现其他框架的载入。

其原理也很简单,就拿@SpringBootApplication注解来说,springboot在启动的时候,就会去识别启动类上的各项注解信息,其中会有一个@EnableAutoConfiguration的注解,点击进入后,可以发现其实是一个实现 DeferredImportSelector 接口的 类

上文就说到@Import可以将一个类加载至spring容器中

观察AutoConfigurationImportSelector类,可以发现,他的核心代码就是重写了DeferredImportSelector中的两个方法,分别是selectImportsgetImportGroup

DeferredImportSelectorImportSelector 的子类。
相对ImportSelector做了额外的扩展,最主要的就是getImportGroup()组的概念。

【疑问】这两个方法的作用是什么?

回答这个问题之前,首先需要需要知道ImportSelector提供的方式为什么不可行。

在上面的验证中,一个类实现ImportSelector并重写selectImports后,在其中返回需要加载的类的全路径数组信息。的确可以进行一个bean的创建与注入容器中。但是紧接着有一个更严重的问题产生。比如下面这种情况:

springboot-jdbc中,默认就会有一个数据库的连接池配置。
但是在实际开发中,通常的配置项采取的是自定义的配置。
如果使用的是ImportSelector进行类的加载与创建,项目框架怎么选择自定义的连接池还是默认的连接池作为首要的配置呢?

【扩充】知识点:

框架本身就有一个默认的配置类,如果开发者进行了自定义的配置,那么在项目中使用的是自定义的配置项,默认的并未进行加载。

【扩充】知识点:

也可能有人会说,在spring的注解中,有一个@ConditionalOnClass的注解,也能实现类似的功能。
如:@ConditionalOnBean(MyDemo2.class)表示如果存在一个MyDemo2的bean,则不会加载@ConditionalOnBean修饰的bean

但是针对这种问题,如果spring依旧还是使用ImportSelector接口的实现类进行大批量的指定bean的加载操作,则可能出现两个bean同时加载的问题。

spring针对bean的加载顺序,并不能保证。
如果先加载@ConditionalOnBean 修饰的bean ,会判断无指定的bean,则会进行创建;
导致后执行的bean也被加载进入容器。

此时,就需要使用到一个ImportSelector的变种配置类DeferredImportSelector。追加了一个分组的概念。

探究 DeferredImportSelector

编写一个测试类,该类实现DeferredImportSelector并重写selectImportsgetImportGroup方法,如下所示:

import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.List;

/**
 * spring在解析 bean定义 时,会通过该方法判断是否 getImportGroup() 有返回值
 */
public class MyDeferredImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"cn.xj.bean2.MyDemo1"};
    }

    /**
     * 如果返回的是null,则会直接调用 cn.xj.test.MyDeferredImportSelector#selectImports 加载对应的bean;
     *
     * 如果返回的是一个自定义的 group 对象,则会加载自定义对象中的 selectImports 进行加载
     * @return
     */
    @Override
    public Class<? extends Group> getImportGroup() {
        return MyGroup.class;
    }

    /**
     * 自定义 group
     */
    private static class MyGroup implements DeferredImportSelector.Group{

        AnnotationMetadata metadata;

        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            this.metadata = metadata;
        }

        @Override
        public Iterable<Entry> selectImports() {
            List<Entry> lists = new ArrayList<>();
            lists.add(new Entry(this.metadata,"cn.xj.bean2.MyDemo2"));
            return lists;
        }

    }
}

创建一个测试类,使用@Import进行类的扫描与注入。逻辑如下所示:

import cn.xj.bean1.MyDemo1;
import cn.xj.bean2.MyDemo2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

@Import(MyDeferredImportSelector.class)
public class MyDeferredImportSelectorTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext(MyDeferredImportSelectorTest.class);
        MyDemo2 bean2 = applicationContext.getBean(MyDemo2.class);
        bean2.test();

        // 异常测试  看拿不拿得到 MyDemo1 的实例
        MyDemo1 bean1 = applicationContext.getBean(MyDemo1.class);
        bean1.test();
    }
}

执行后,观察控制台打印效果。
在这里插入图片描述
从日志中很清楚的可以看到:

如果重写了getImportGroup(),并返回了一个自定义的 Group 类
在spring解析bean定义类时,会先进行getImportGroup的返回值判断。
如果返回的是一个null,则直接将cn.xj.test.MyDeferredImportSelector#selectImports的返回值进行解析。
如果返回的事一个自定义的group,则会直接去cn.xj.test.MyDeferredImportSelector.MyGroup#selectImports进行bean的解析。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解放研究者:GPT自动化科研

GPT Researcher 是一个自主代理程序&#xff0c;旨在进行多种任务的全面在线研究。 该代理能够生成详细、事实性和公正的研究报告&#xff0c;并提供个性化选项&#xff0c;以便关注相关资源、大纲和教训。受到AutoGPT和最近的Plan-and-Solve论文的启发&#xff0c;GPT Researc…

图像标注是什么?及其类型和应用

什么是图像标注&#xff1f; 图像标注是与您交互的许多人工智能产品的基础&#xff0c;并且是计算机视觉&#xff08;CV&#xff09;领域重要的过程之一。在图像标注过程中&#xff0c;数据标注员使用标签或元数据来标记AI模型学习识别的数据特征。然后&#xff0c;这些图像标…

线程池学习(六)线程池状态转化

线程池状态定义 // runState is stored in the high-order bits // 线程池创建之后的初始状态&#xff0c;这种状态下可以执行任务private static final int RUNNING -1 << COUNT_BITS; // 线程池不再接收新的任务&#xff0c;但是会将队列中的任务执行完 private s…

解决apkanalyzer.bat could NOT be found in D:\Download\Android SDK Tools!警告报错

appium安装过程中很可能出现以下警告报错&#xff0c;咱就按如下操作即可搞定&#xff01;&#xff01;&#xff01; apkanalyzer.bat could NOT be found in D:\Download\Android SDK Tools! 一、下载Command line tools 下载地址&#xff1a;​https://developer.android.g…

Jenkins (一)

Jenkins (一) Docker Jenkins 部署 一. 安装 jenkins $ mkdir -p /home/tester/data/docker/jenkins $ vim jenkins:lts-jdk11.sh./jenkins:lts-jdk11.sh 内容 #! /bin/bash mkdir -p /home/tester/data/docker/jenkins/jenkins_homesudo chown -R 1000:1000 /home/tester/da…

基于simulink的DPLL仿真笔记

该笔记主要用于本人思路整理与记录 本设计运用的是电荷泵一阶环路滤波器&#xff0c;二阶三阶则在此基础上举一反三&#xff0c;以后如有机会会慢慢补全 文章目录 一.仿真模型PS&#xff08;题外话&#xff09; 二.仿真结果三.环路滤波器分析1. 环路滤波器对比LPF2. 环路滤波器…

从零开发短视频电商 单元测试(TestNG)

文章目录 简介简单示例执行测试并查看测试报告方式一 在IDEA中运行testng.xml文件方式二 在IDEA中运行测试类或者package方式三 在Maven中运行测试 统计测试覆盖率方式一 IDEA 支持详细的代码测试覆盖率统计方式二 Maven支持测试覆盖率 在IDEA中创建测试用例使用 IDEA 快速创建…

ELK搭建

ELK介绍&#xff1a; ELK是一组开源工具的缩写&#xff0c;它由Elasticsearch、Logstash和Kibana三个组件组成&#xff0c;用于处理、分析和可视化大量日志数据。 入门级ELK搭建&#xff08;无Docker环境&#xff09; 安装前准备 1.获取安装包 https://artifacts.elastic…

【InsCode Stable Diffusion 美图活动一期】生成着玩

此为内容创作模板&#xff0c;请按照格式补充内容&#xff0c;在发布之前请将不必要的内容删除 一、 Stable Diffusion 模型在线使用地址&#xff1a; https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型相关版本和参数配置&#xff1a; 三、图片生成提示词与反向…

【Docker】详解docker安装及使用

详解docker安装及使用 1. 安装docker1.1 查看docker版本信息 2. Docker镜像操作3. Docker容器操作4.知识点总结4.1 docker镜像操作4.2 docker容器操作4.3 docker run启动过程 参见docker基础知识点详解 1. 安装docker 目前Docker只能支持64位系统。 ###关闭和禁止防火墙开机自…

Hadoop: High Available

序言 在Hadoop 2.X以前的版本&#xff0c;NameNode面临单点故障风险&#xff08;SPOF&#xff09;&#xff0c;也就是说&#xff0c;一旦NameNode节点挂了&#xff0c;整个集群就不可用了&#xff0c;而且需要借助辅助NameNode来手工干预重启集群&#xff0c;这将延长集群的停…

Windows 组策略 部署打印机

一、服务端 1、打印机管理&#xff1a;添加打印机 2、选择打印机 3、第一次安装&#xff0c;选择这个 4、下载驱动&#xff0c;从磁盘安装 5、已成功安装 6、选中打印机右击属性&#xff1a;列出目录 7、创建一个组策略 8、组策略设置 用户设置 → 首选项 → 控制面板 → 打印…

C++day4 (拷贝构造函数、拷贝赋值函数、匿名对象、友元函数、常成员函数、常对象、运算符重载)

#include <iostream> #include <cstring> using namespace std;class mystring { private:char *str; //记录C风格字符串int size; //记录字符串的实际长度public://无参构造mystring():size(10){strnew char[size];//构造出一个长度为10的字符串strcpy(str,&…

22.代理模式

代理模式 二十三种设计模式中的一种&#xff0c;属于结构型模式。它的作用就是通过提供一个代理类&#xff0c;在调用目标方法的时候&#xff0c;不再是直接对目标方法进行调用&#xff0c;而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦…

移动端深度学习部署:TFlite

1.TFlite介绍 &#xff08;1&#xff09;TFlite概念 tflite是谷歌自己的一个轻量级推理库。主要用于移动端。 tflite使用的思路主要是从预训练的模型转换为tflite模型文件&#xff0c;拿到移动端部署。 tflite的源模型可以来自tensorflow的saved model或者frozen model,也可…

初识protobuf

Protobuf 全称Protocol Buffers&#xff08;协议缓冲区&#xff09;&#xff0c;是一种轻量级、高效的数据序列化格式&#xff0c;由Google开发。它被设计用于结构化数据的序列化、反序列化以及数据交换&#xff0c;常用于网络通信和数据存储等领域。 Protobuf使用简洁的消息描…

解决appium-doctor报 bundletool.jar cannot be found

一、下载bundletool.jar 下载地址&#xff1a;https://github.com/google/bundletool/releases 二、重命名 重命名这个jar包为bundletool.jar&#xff0c;在android sdk目录下&#xff0c;新建bundle-tool目录&#xff0c;把bundletool.jar包放入其中。 三、配置环境 path后追加…

re学习(19)[ACTF新生赛2020]easyre1(UPX脱壳)

文章链接&#xff1a;BUUCTF在线评测 参考视频&#xff1a;B站 【新手教程三】小Z带你学习什么是ESP定律和什么是堆栈平衡 &#xff1f; - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn 题解&#xff1a; 工具脱壳 key"*F\"N,\"…

蒲公英打包环境搭建碰到问题

一&#xff1a;证书那边选择手动&#xff0c;不要自动&#xff0c;——》debug配置dev证书&#xff0c;release配置ad-hoc证书 二&#xff1a;证书有时候不生效&#xff0c;删除重新下载。~/Library/MobileDevice/Provisioning Profiles 三&#xff1a;更新测试手机时&#…

# Linux终端控制字符详解以及简单应用实践

Linux终端控制字符详解以及简单应用实践 文章目录 Linux终端控制字符详解以及简单应用实践1 控制字符表2 控制字符 ESC &#xff08;0x1B&#xff0c;^[&#xff09;子参数表3 控制字符 ESC &#xff08;0x1B&#xff0c;^[&#xff09;子参数表 - 字符颜色参照表4 实践&#x…