SpringBoot原理-03.自动配置-方案

news2025/4/19 15:34:27

一.自动配置原理

探究自动配置原理,就是探究spring是如何在运行时将要依赖JAR包提供的配置类和bean对象注入到IOC容器当中。我们当前准备一个maven项目itheima-utils,这里面定义了bean对象以及配置类,用来模拟第三方提供的依赖,首先进行导入。如果我们要使用这个第三方依赖,就要首先将其引入进来。

<dependency>
            <groupId>com.example</groupId>
            <artifactId>itheima-utils</artifactId>
            <version>0.0.1-SNAPSHOT</version>
</dependency>

 

我们在itheima-utils中可以发现,有一个配置类HeaderConfig,上面加上了一个注解@Configuration,表明当前类是一个配置类,里面有两个配置方法HeaderParser和HeaderGenerator,用来返回HeaderParser和HeaderGenerator的实例化对象,上面加上了@Bean注解,表明将这两个类作为IOC容器当中的bean对象。

package com.example;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class HeaderConfig {

    @Bean
    public HeaderParser headerParser(){
        return new HeaderParser();
    }

    @Bean
    public HeaderGenerator headerGenerator(){
        return new HeaderGenerator();
    }
}
package com.example;

public class HeaderParser {

    public void parse(){
        System.out.println("HeaderParser ... parse ...");
    }

}
package com.example;

public class HeaderGenerator {

    public void generate(){
        System.out.println("HeaderGenerator ... generate ...");
    }

}

我们还定义了一个普通类TokenParser,在该类上面加上了一个注解@Component。

package com.example;

import org.springframework.stereotype.Component;

@Component
public class TokenParser {

    public void parse(){
        System.out.println("TokenParser ... parse ...");
    }

}

然后我们在springboot-web-config2中的pom文件中导入itheima-utils的依赖,然后在其测试类中编写测试代码,查看是否能够将IOC容器当中的bean对象引入进来。

package com.gjw.springbootwebconfig2;

import com.example.HeaderGenerator;
import com.example.HeaderParser;
import com.example.TokenParser;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

@SpringBootTest
public class AutoConfigTests {
    @Autowired
    private ApplicationContext applicationContext;


    // 测试第三方依赖下面的bean对象
    @Test
    void tokenParserTest() {
        System.out.println(applicationContext.getBean(TokenParser.class));
    }

    @Test
    void headerParserTest() {
        System.out.println(applicationContext.getBean(HeaderParser.class));
    }

    @Test
    void headerGeneratorTest() {
        System.out.println(applicationContext.getBean(HeaderGenerator.class));
    }
}

通过applicationContext这个IOC容器对象将TokenParser等bean对象引入进来,我们发现报错了,没有找到TokenParser这个bean对象

HeaderParser这个bean对象也没有找到。

那么为什么没有找到呢?

我们在类上加上了@Compoment注解来声明bean对象,这个注解就一定会生效么?并不是,这个注解还要被spring组件扫描到才行,而我们前面提到,在springboot项目当中,@SpringBootApplication具有包扫描的作用,但是其扫描范围仅仅是当前启动类所在包及其子包,也就是说根据当前的目录结构,他只会扫描com.gjw这个包下的类中定义的bean对象以及com.gjw这个包下子包中的类中定义的bean对象。

 因此是扫描不到itheima-utils这个第三方依赖中所定义的bean对象的,所以找不到该bean对象。那么如何设置其扫描范围,让其扫描得到呢?

二.@ComponentScan组件扫描

我们可以在当前要运行的springboot项目的启动类上加上一个注解@ComponentScan来配置要扫描的范围。原来我们的扫描范围只是com.gjw,这是默认的。现在指定了要扫描com.example这个包,那么原来默认的包就要显式的指定出来。

在@ComponentScan这个注解当中我们可以通过value属性或者basePackages这个属性来指定我们要扫描哪些包?而这个属性返回值是一个数组,我们就在数组当中指定我们要扫描到的包是哪些。

package com.gjw;

import ch.qos.logback.core.joran.event.SaxEvent;
import com.example.EnableHeaderConfig;
import com.example.HeaderConfig;
import com.example.MyImportSelector;
import com.example.TokenParser;
import org.dom4j.io.SAXReader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@ComponentScan({"com.gjw","com.example"})   // 方式一:采用@ComponentScan指定bean对象的扫描范围


@SpringBootApplication    
public class SpringbootWebConfig2Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

这样再次运行测试方法,发现已经成功获取到了IOC容器当中的bean对象。但是如果要扫描的包特别多,那么使用起来将非常繁琐,进而影响性能。

三.@Import导入

使用@Import导入主要的形式有三种:

1.导入普通类

2.导入配置类

3.导入ImportSelector接口实现类

我们依次来看

首先我们导入普通类,当前com.itheima这个包下有一个普通类TokenParser,上面加上了@Component注解,那么可以使用@Import注解将其导入。在@Import注解解释中如下。    

在@Import注解当中我们可以使用value属性导入一个常规类。也可以导入一个被@Configuration标识的配置类,也可以导入ImportSelector这个接口的实现类,还可以导入ImportBeanDefinitionRegistrar这个接口的实现类。

1.导入普通类 

首先导入普通类TokenParser

package com.gjw;

import ch.qos.logback.core.joran.event.SaxEvent;
import com.example.EnableHeaderConfig;
import com.example.HeaderConfig;
import com.example.MyImportSelector;
import com.example.TokenParser;
import org.dom4j.io.SAXReader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

//  @ComponentScan({"com.gjw","com.example"})   方式一:采用@ComponentScan指定bean对象的扫描范围

/**
 * 方式二:可以使用@Import注解
 *      可以用于:1.普通类
 *              2.配置类
 *              3.ImportSelector接口的实现类
 *              4.@EnableXXXX注解,封装@Import注解
 */
@Import({TokenParser.class})    // 普通类
@SpringBootApplication    
public class SpringbootWebConfig2Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }


}

 发现TokenParser这个类的bean对象已经存在。

2.导入配置类

然后我们导入配置类,

package com.gjw;

import ch.qos.logback.core.joran.event.SaxEvent;
import com.example.EnableHeaderConfig;
import com.example.HeaderConfig;
import com.example.MyImportSelector;
import com.example.TokenParser;
import org.dom4j.io.SAXReader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

//  @ComponentScan({"com.gjw","com.example"})   方式一:采用@ComponentScan指定bean对象的扫描范围

/**
 * 方式二:可以使用@Import注解
 *      可以用于:1.普通类
 *              2.配置类
 *              3.ImportSelector接口的实现类
 *              4.@EnableXXXX注解,封装@Import注解
 */
@Import({TokenParser.class})    // 普通类
@Import({HeaderConfig.class})         // 配置类

@SpringBootApplication    
public class SpringbootWebConfig2Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

打开测试类,在测试类当中运行测试方法,发现HeaderParser和HeaderGenerator的bean对象已经存在。

3.导入ImportSelector接口实现类

我们查看ImportSelector这个接口的源码。这个接口中有一个接口方法selectImports,返回值是一个String类型的数组,封装的是类名,封装的就是我们要将哪些类交给IOC容器管理,可以将这些类的全类名封装在数组中返回即可。

首先我们定义ImportSelector这个接口的实现类,

package com.example;

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

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.HeaderConfig"};
    }
}

既然selectImports这个方法中要的是全类名,那么我们可以将这些类定义到一个文件当中,要加载哪些类直接添加进去读取即可。读取之后封装到这个String数组中就行。这里我们指定将HeaderConfig这个类交给IOC容器管理。

然后我们使用@Import这个注解将其导入即可。

package com.gjw;

import ch.qos.logback.core.joran.event.SaxEvent;
import com.example.EnableHeaderConfig;
import com.example.HeaderConfig;
import com.example.MyImportSelector;
import com.example.TokenParser;
import org.dom4j.io.SAXReader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

//  @ComponentScan({"com.gjw","com.example"})   方式一:采用@ComponentScan指定bean对象的扫描范围

/**
 * 方式二:可以使用@Import注解
 *      可以用于:1.普通类
 *              2.配置类
 *              3.ImportSelector接口的实现类
 *              4.@EnableXXXX注解,封装@Import注解
 */
@Import({TokenParser.class})    // 普通类
@Import({HeaderConfig.class})         // 配置类
@Import(MyImportSelector.class)   // MyImportSelector里面定义了要生成的bean对象
@SpringBootApplication    
public class SpringbootWebConfig2Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }

}

4.@EnableXxxx注解,封装@Import注解

但是如果基于上面的方式,以后我们在引入一个第三方依赖所提供的bean和配置类,每一次都要知道我们要引入哪些bean和配置类。但是我们知道吗?不知道,只有第三方依赖最清楚。常见的方案是第三方依赖会给我们提供一个注解,这个注解一般都是@Enable打头,注解中封装的就是@Import注解。在@Import后面再指定要导入哪些bean和配置类。

如itheima-utils依赖中就提供了@EnableHeaderConfig这个注解,

package com.example;

import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface EnableHeaderConfig {
}

上面加了注解@Import,@Import中指定我们要导入哪些配置类。而在MyImportSelector.class这个类中就制定了我们要导入哪些配置类。现在我们要指定导入哪些bean,直接在启动类上加上@EnableHeaderConfig这个注解即可。

package com.gjw;

import ch.qos.logback.core.joran.event.SaxEvent;
import com.example.EnableHeaderConfig;
import com.example.HeaderConfig;
import com.example.MyImportSelector;
import com.example.TokenParser;
import org.dom4j.io.SAXReader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

//  @ComponentScan({"com.gjw","com.example"})   方式一:采用@ComponentScan指定bean对象的扫描范围

/**
 * 方式二:可以使用@Import注解
 *      可以用于:1.普通类
 *              2.配置类
 *              3.ImportSelector接口的实现类
 *              4.@EnableXXXX注解,封装@Import注解
 */
//@Import({TokenParser.class})    // 普通类
//@Import({HeaderConfig.class})         // 配置类
//@Import(MyImportSelector.class)   // MyImportSelector里面定义了要生成的bean对象
@EnableHeaderConfig     // @EnableHeaderConfig继承封装了MyImportSelector
@SpringBootApplication    
public class SpringbootWebConfig2Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

测试后HeaderParser这个bean对象存在。

第四种更方面优雅。

 

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

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

相关文章

(KTransformers) RTX4090单卡运行 DeepSeek-R1 671B

安装环境为&#xff1a;ubuntu 22.04 x86_64 下载模型 编辑文件vim url.list 写入如下内容 https://modelscope.cn/models/unsloth/DeepSeek-R1-GGUF/resolve/master/DeepSeek-R1-Q4_K_M/DeepSeek-R1-Q4_K_M-00001-of-00009.gguf https://modelscope.cn/models/unsloth/Dee…

【软考-架构】1.2、指令系统-存储系统-cache

GitHub地址&#xff1a;https://github.com/tyronczt/system_architect ✨资料&文章更新✨ 指令系统 计算机指令执行过程&#xff1a;取指令一一分析指令一一执行指令三个步骤&#xff0c;首先将程序计数器PC中的指令地址取出&#xff0c;送入地址总线&#xff0c;CPU依据…

动态规划刷题

文章目录 动态规划三步问题题目解析代码 动态规划 1. 状态表示&#xff1a;dp[i]&#xff0c;表示dp表中i下标位置的值 2. 状态转移方程&#xff1a;以i位置位置的状态&#xff0c;最近的一步来划分问题&#xff0c;比如可以将状态拆分成前状态来表示现状态&#xff0c;dp[i] …

2025-03-01 学习记录--C/C++-C语言 整数类型对比

C语言 整数类型对比 类型位数范围&#xff08;有符号&#xff09;范围&#xff08;无符号&#xff09;格式化符号char8-128 到 1270 到 255%c 或 %hhdshort16-32,768 到 32,7670 到 65,535%hdint32-2,147,483,648 到 2,147,483,6470 到 4,294,967,295%dlong32 或 64-2,147,483…

爬虫系列之【数据解析之正则】《二》

目录 前言 一、正则基本使用 1.1 导包 1.2 接口方法 1.3 换行匹配问题 二、实战案例 完整代码 前言 在爬虫工作中&#xff0c;我们主要会遇到两种类型的文本数据&#xff1a; JSON格式数据 HTML文档数据 对于JSON字符串数据&#xff0c;通常使用Python的字典操作进行键…

RFID工具柜DW-G104R|智能存储,便捷高效

一、行业背景 RFID智能工具柜&#xff08;DW-G104R&#xff09;RFID工具管理柜是一种结合RFID技术和智能柜设备的新型工具管理设施&#xff0c;通过自动化管理可以提高工具管理的效率和准确性。 在工业生产中&#xff0c;工具柜是工具存储和管理的重要设备。传统工具柜存在管…

Linux软连接与时区日期

软连接 使用ln命令创建软连接。 在系统中创建软连接&#xff0c;可以将文件&#xff0c;文件夹连接到其他为止。 类似于Windows系统的快捷方式。 语法&#xff1a;ln -s 参数1 参数2 -s选项&#xff0c;创建软连接。 参数1&#xff0c;被链接的文件或文件夹。 参数2&#xff0…

2024 ChatGPT大模型技术场景与商业应用视频精讲合集(45课).zip

2024ChatGPT大模型技术场景与商业应用视频精讲合集&#xff0c;共十三章&#xff0c;45课。 01. 第一章 ChatGPT&#xff1a;通用人工智能的典范 1.1 ChatGPT概述 .mp4 1.2 通用能力 .mp4 1.3 通用人工智能风口 .mp4 02. 第二章 大模型&#xff1a;ChatGPT的核心支撑 2.1 底层…

HTTP四次挥手是什么?

四次挥手&#xff0c;这是TCP协议用来关闭连接的过程。四次挥手是确保两个主机之间能够安全、可靠地关闭连接的重要机制。我会用简单易懂的方式来讲解&#xff0c;帮助你理解它的原理和过程。 1. 什么是四次挥手&#xff1f; 定义 四次挥手是TCP协议用来关闭连接的过程。它通…

人工智能之数学基础:线性代数中的特殊矩阵

本文重点 矩阵是数学中一个重要的工具,在各个领域都有广泛的应用。其中,一些特殊矩阵由于具有独特的性质,在特定的问题中发挥着关键作用。 单位矩阵 单位矩阵是一种特殊的方阵,在矩阵乘法中起到类似于数字 “1” 的作用。对于一个的单位矩阵,其主对角线元素全为 1,其余…

Linux篇——工具

在有了前面的基础知识后&#xff0c;我们现在基本能够使用Linux的相关基本操作了&#xff0c;但我们知道&#xff0c;没有工具我们是无法便捷地实现某些功能的&#xff0c;因此我们这篇内容来谈谈Linux中的工具。 一、软件包管理器yum 我们知道&#xff0c;我们要想获得一个软…

leetcode第77题组合

原题出于leetcode第77题https://leetcode.cn/problems/combinations/ 1.树型结构 2.回溯三部曲 递归函数的参数和返回值 确定终止条件 单层递归逻辑 3.代码 二维数组result 一维数组path void backtracking(n,k,startindex){if(path.sizek){result.append(path);return ;}…

Nacos + Dubbo3 实现微服务的Rpc调用

文章目录 概念整理基本概念概念助记前提RPC与HTTP类比RPC接口类的一些理解 实例代码主体结构父项目公共接口项目提供者项目项目结构POM文件实现配置文件实现公共接口实现程序入口配置启动项目检查是否可以注入到Nacos 消费者项目项目结构POM文件实现配置文件实现注册RPC服务类实…

算法-数据结构(图)-弗洛伊德算法复现(Floyd)

弗洛伊德算法&#xff08;Floyd-Warshall算法&#xff09;是一种用于求解所有节点对最短路径的动态规划算法&#xff0c;适用于有向图或无向图&#xff0c;且能处理带有负权边的图&#xff08;但不能有负权环&#xff09;。该算法的时间复杂度为 O(V3)O(V3)&#xff0c;其中 VV…

51c自动驾驶~合集22

我自己的原文哦~ https://blog.51cto.com/whaosoft/11870502 #自动驾驶数据闭环最前沿论文 近几年&#xff0c;自动驾驶技术的发展日新月异。从ECCV 2020的NeRF问世再到SIGGRAPH 2023的3DGS&#xff0c;三维重建走上了快速发展的道路&#xff01;再到自动驾驶端到端技术的…

基于javaweb的SpringBoot在线动漫信息平台系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

【Qt】MVC设计模式

目录 一、搭建MVC框架 二、创建数据库连接单例类SingleDB 三、数据库业务操作类model设计 四、control层&#xff0c;关于model管理类设计 五、view层即为窗口UI类 一、搭建MVC框架 里面的bin、lib、database文件夹以及sqlite3.h与工程后缀为.pro文件的配置与上次发的文章…

ARM 处理器平台 eMMC Flash 存储磨损测试示例

By Toradex秦海 1). 简介 目前工业嵌入式 ARM 平台最常用的存储器件就是 eMMC Nand Flash 存储&#xff0c;而由于工业设备一般生命周期都比较长&#xff0c;eMMC 存储器件的磨损寿命对于整个设备来说至关重要&#xff0c;因此本文就基于 NXP i.MX8M Mini ARM 处理器平台演示…

本地部署DeepSeek-R1(Dify发件邮箱、找回密码、空间名称修改)

Dify配置发件邮箱 DIfy默认邮箱配置为空&#xff0c;在邀请团队成员注册时是不会发送邀请链接的&#xff0c;只能通过手动复制生成的注册链接发送给对应的人去注册设置密码。 这样很麻烦&#xff0c;并且在找回密码时也接收不了邮件&#xff0c;无法重置密码。 找到本地部署…

EasyRTC:支持任意平台设备的嵌入式WebRTC实时音视频通信SDK解决方案

随着互联网技术的飞速发展&#xff0c;实时音视频通信已成为各行各业数字化转型的核心需求之一。无论是远程办公、在线教育、智慧医疗&#xff0c;还是智能安防、直播互动&#xff0c;用户对低延迟、高可靠、跨平台的音视频通信需求日益增长。 一、WebRTC与WebP2P&#xff1a;实…