当前位置: 首页 > article >正文

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

一.自动配置原理

探究自动配置原理,就是探究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 {@Beanpublic HeaderParser headerParser(){return new HeaderParser();}@Beanpublic 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 {@Autowiredprivate ApplicationContext applicationContext;// 测试第三方依赖下面的bean对象@Testvoid tokenParserTest() {System.out.println(applicationContext.getBean(TokenParser.class));}@Testvoid headerParserTest() {System.out.println(applicationContext.getBean(HeaderParser.class));}@Testvoid 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对象存在。

第四种更方面优雅。

 

相关文章:

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

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

Python 如何实现烟花效果的完整代码

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…...

Winbox5怎样设置上网

要让连接到交换机的设备都能上网&#xff0c;需在MikroTik路由器&#xff08;通过WinBox配置&#xff09;上完成以下步骤。假设您的网络拓扑是&#xff1a;外网IP配置在路由器的WAN口&#xff0c;内网设备通过交换机连接到路由器的LAN口。 步骤 1&#xff1a;配置内网接口&…...

(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依据…...

CI/CD与容器化技术核心知识点的QA

CI/CD与容器化技术核心知识点的Q&A 以下是CI/CD和容器化技术的经典必考面试题及详解,结合行业实践与理论核心整理而成: 一、CI/CD核心概念与流程 简述CI/CD的核心目标与实现价值 答案:通过自动化流水线实现快速、可靠的软件交付,减少人工干预,提升开发与运维协作效率…...

动态规划刷题

文章目录 动态规划三步问题题目解析代码 动态规划 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…...

Python核心技术,Django学习基础入门教程(附环境安装包)

文章目录 前言1. 环境准备1.1Python安装1.2选择Python开发环境1.3 创建虚拟环境1.4 安装 Django 2. 创建 Django 项目3. Django项目结构介绍4. 启动开发服务器5. 创建 Django 应用6. 应用结构介绍7. 编写视图函数8. 配置 URL 映射9. 运行项目并访问视图10. 数据库配置与模型创建…...

MFC中CMutex类和CSingleLock类,配合使用疑惑

在使用CMutex过程中&#xff0c;看到别人使用了CSingleLock类&#xff0c;想着明明CMutex已经可以实现线程同步了&#xff0c;为什么还有使用CSingleLock类呢&#xff1f; 在MFC中&#xff0c;虽然CMutex类本身可以实现线程同步&#xff0c;但通常会与CSingleLock类一起使用&am…...

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

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

HTML AI 编程助手

HTML AI 编程助手 引言 随着人工智能技术的飞速发展&#xff0c;编程领域也迎来了新的变革。HTML&#xff0c;作为网页制作的基础语言&#xff0c;与AI技术的结合&#xff0c;为开发者带来了前所未有的便利。本文将探讨HTML AI编程助手的功能、应用场景以及如何利用它提高编程…...

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

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

【前端面试】如何不通过正则:验证IP地址合法性

前言 在 Web 开发中&#xff0c;验证用户输入的 IP 地址是否合法是一个常见需求 面试中也会问到 通常&#xff0c;我们会使用正则表达式来完成这个任务&#xff0c;因为它简洁高效。然而&#xff0c;正则表达式对于初学者来说可能有些晦涩难懂。本文将介绍一种不使用正则表达…...

中间件专栏之Redis篇——Redis的三大持久化方式及其优劣势对比

Redis是内存数据库&#xff0c;它的数据一般存放在内存中&#xff0c;一旦断电或者宕机&#xff0c;存在内存中的数据就会丢失。当然&#xff0c;它也具备数据持久化的能力&#xff0c;本文就将介绍Redis的三种持久化方式及其优劣势对比。 一、RDB&#xff08;Redis Database&…...

Linux软连接与时区日期

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

安全测试之五:SQL Server注入漏洞几个实例

示例 1&#xff1a;在 GET 请求中测试 SQL 注入 最简单且有时最有效的情况是针对登录页面进行测试。当登录页面请求用户输入用户名和密码时&#xff0c;攻击者可以尝试输入以下字符串 “ or 11”&#xff08;不包含双引号&#xff09;&#xff1a; https://vulnerable.web.ap…...

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协议用来关闭连接的过程。它通…...

前端内存泄漏的几种情况及方案

前端内存泄漏是常见但容易被忽视的问题&#xff0c;可能导致页面卡顿、崩溃或性能下降。以下是几种典型场景及解决方案&#xff1a; 1. 未清理的全局变量 场景&#xff1a; 意外创建全局变量&#xff08;未使用 var/let/const&#xff09;。主动挂载到 window 的大对象未释放…...

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

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

单例模式---是 Spring 容器的核心特性之一

1.最近面试让手写一个单例&#xff1b;我一直知道单例&#xff1b;但是一直很困惑&#xff1b;工作中也没怎么用过&#xff1b;为什么面试总问&#xff1b;今天我才知道思考出来&#xff1b;单例是spring容器的核心特性&#xff1b;很多知识我只知道是什么&#xff1b;但是没有…...

代码随想录算法【Day59】

47. 参加科学大会 思路 使用Dijkstra 算法 计算从起点&#xff08;节点 1&#xff09;到终点&#xff08;节点 n&#xff09;的最短路径。用优先队列&#xff08;小顶堆&#xff09; 维护当前未访问的最短路径节点&#xff0c;每次选择距离最短的未访问节点进行更新&#xff…...

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 ;}…...

通过fgets获取文件内容

#include <stdio.h> char *fgets(char *s, int size, FILE *stream); 使用fgets获取文件内容 #include <stdio.h> #include <stdlib.h>int main(void) {char str[100] {0};FILE *fp NULL;fp fopen("./test_file", "r");if (NULL …...

STaR(Self-Taught Reasoner)方法:让语言模型自学推理能力(代码实现)

STaR&#xff08;Self-Taught Reasoner&#xff09;方法&#xff1a;让语言模型自学推理能力 在大型语言模型&#xff08;LLM&#xff09;的推理能力优化中&#xff0c;STaR&#xff08;Self-Taught Reasoner&#xff09; 是一种引人注目的技术&#xff0c;属于“修改提议分布…...

[创业之路-329]:华为铁三角实施的步骤

一、通用过程 华为铁三角实施的步骤主要包括以下几个关键阶段&#xff1a; 1、明确角色与职责 确定铁三角成员&#xff1a;组建由客户经理&#xff08;AR&#xff09;、解决方案经理&#xff08;SR&#xff09;和交付经理&#xff08;FR&#xff09;组成的铁三角团队。制定岗…...

在 Ubuntu 下通过 Docker 部署 Caddy 和 PHP-FPM 服务器

引言 大家好&#xff0c;今天我们要聊的主题是如何在 Ubuntu 上通过 Docker 部署 Caddy 和 PHP-FPM 服务器。Caddy 是一个现代化的 web 服务器&#xff0c;支持 HTTPS&#xff0c;配置简单&#xff1b;而 PHP-FPM 是 PHP 的 FastCGI 进程管理器&#xff0c;能够高效处理 PHP 请…...