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对象存在。

第四种更方面优雅。

 

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

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

相关文章

高频 SQL 50 题(基础版)_2356. 每位教师所教授的科目种类的数量

高频 SQL 50 题&#xff08;基础版&#xff09;_2356. 每位教师所教授的科目种类的数量 select teacher_id ,count(distinct(subject_id)) as cnt from Teacher group by teacher_id

神经网络之词嵌入模型(基于torch api调用)

一、Word Embedding&#xff08;词嵌入&#xff09;简介 Word Embedding&#xff08;词嵌入&#xff09;&#xff1a; 词嵌入技术是自然语言处理&#xff08;NLP&#xff09;领域的一项重大创新&#xff0c;它极大地推动了计算机理解和处理人类语言的能力。 通过将单词、句子甚…

SpringBoot @Value 注解使用

Value 注解用于将配置文件中的属性值注入到Spring管理的Bean中。 1. 基本用法 Value 可以直接注入配置文件中的属性值。 配置文件 (application.properties 或 application.yml) 配置文件定义需要注入的数据。 consumer:username: lisiage: 23hobby: sing,read,sleepsubje…

Redis面试常见问题——使用场景问题

目录 Redis面试常见问题 如果发生了缓存穿透、击穿、雪崩&#xff0c;该如何解决&#xff1f; 缓存穿透 什么是布隆过滤器&#xff1f; 缓存击穿 缓存雪崩 双写一致性&#xff08;redis做为缓存&#xff0c;mysql的数据如何与redis进行同步呢&#xff1f;&#xff09; …

MySQL中的共享锁和排他锁

MySQL 中的锁可以从多个维度进行分类&#xff0c;其中从模式上可以分为共享锁&#xff08;Shared Lock&#xff0c;S Lock&#xff09;和 排他锁&#xff08;Exclusive Lock&#xff0c;X Lock&#xff09;。 共享锁&#xff08;Shared Lock&#xff0c;S Lock&#xff09; 共…

The “Rule-of-Zero“ should be followed (s4963)

Most classes should not directly handle resources, but instead, use members that perform resource handling for them: For memory, it can be std::unique_ptr, std::shared_ptr, std::vector…​For files, it can be std::ofstream, std::ifstream…​…​ Classes …

在Ubuntu 22.04 LTS 上安装 MySQL两种方式:在线方式和离线方式

Ubuntu安装MySQL 介绍&#xff1a; Ubuntu 是一款基于Linux操作系统的免费开源发行版&#xff0c;广受欢迎。它以稳定性、安全性和用户友好性而闻名&#xff0c;适用于桌面和服务器环境。Ubuntu提供了大量的软件包和应用程序&#xff0c;拥有庞大的社区支持和活跃的开发者社区…

用Java编写sql

1.概念 通过Java代码操作mysql数据库 数据库编程&#xff0c;是需要数据库服务器&#xff0c;提供一些API&#xff0c;供程序员调用的 2.安装 2.1下载 在程序中操作mysql需要先安装mysql的驱动包 并且要把驱动包引入到项目中 在中央仓库可以下载到驱动包(mvnrepository.…

在Ubuntu中,某个文件的右下角有一把锁的标志是什么意思?

在Ubuntu中&#xff0c;某个文件的右下角有一把锁的标志是什么意思&#xff1f; 在 Ubuntu&#xff08;或其他基于 GNOME 文件管理器的 Linux 发行版&#xff09;中&#xff0c;文件或文件夹的右下角出现一把“锁”标志&#xff0c;通常表示 你当前的用户没有该文件/文件夹的写…

Redis数据结构-List列表

1.List列表 列表类型适用于存储多个有序的字符串&#xff08;这里的有序指的是强调数据排列顺序的重要&#xff0c;不是升序降序的意思&#xff09;&#xff0c;列表中的每个字符串称为元素&#xff08;element&#xff09;&#xff0c;一个列表最多可以存储2^32-1个元素。在R…

《论负载均衡技术在Web系统中的应用》审题技巧 - 系统架构设计师

软考论文写作框架 一、考点概述 本题考点主要围绕“负载均衡技术在Web系统中的应用”展开,旨在考察考生对负载均衡技术的理解、应用及项目管理经验。负载均衡技术是提升Web系统性能的关键手段,通过合理分配和分散系统负载,确保多个操作单元能够高效协同工作,从而提升系统…

Linux实操——在服务器上直接从百度网盘下载(/上传)文件

Linux Linux实操——在服务器上直接从百度网盘下载&#xff08;/上传&#xff09;文件 文章目录 Linux前言一、下载并安装bypy工具二、认证并授权网盘账号三、将所需文件转移至目的文件夹下四、下载文件五、上传文件六、更换绑定的百度云盘账户 前言 最近收到一批很大的数据&…

报错The default superclass, “jakarta.servlet.http.HttpServlet“(已经配置好tomcat)

报错报错DescriptionResourcePathLocationType The default superclass,“jakarta.servlet.http.HttpServlet”, according to the project’s Dynamic Web Module facet version (5.0), was not found on the Java Build Path. 解决办法&#xff1a; 根据错误信息&#xff0…

【UI设计——陕西红富士苹果海报分享】

陕西红富士苹果海报设计分享 为大家带来一款陕西红富士苹果的宣传海报设计。 海报以柔和的粉色为背景&#xff0c;营造出温馨的氛围。画面下方展示了色泽红润、形态饱满的红富士苹果&#xff0c;既有完整的果实&#xff0c;也有切开的剖面&#xff0c;直观呈现其诱人外观。 上…

题解 | 牛客周赛82 Java ABCDEF

目录 题目地址 做题情况 A 题 B 题 C 题 D 题 E 题 F 题 牛客竞赛主页 题目地址 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 做题情况 A 题 判断字符串第一个字符和第三个字符是否相等 import java.io.*; import java.math.*; import java.u…

vulkanscenegraph显示倾斜模型(5)-视景器准备

前言 本文在接着往下讨论视景器准备相关步骤。Vulkan相比opengl更底层,其提供了更底层的硬件控制、更高的性能以及更好的多线程支持,VSG 通过封装 Vulkan 的复杂性,提供了更简单易用的接口,同时保留了 Vulkan 的高性能和灵活性。它简化了 Vulkan 的初始化、渲染管线配置、资…

基金 word-->pdf图片模糊的解决方法

1. 首先需要Adobe或福昕等pdf阅读器。 2. word中 [文件]--[打印]&#xff0c;其中打印机选择pdf阅读器&#xff0c;例如此处我选择福昕阅读器。 3. 选择 [打印机属性]--[编辑]--[图像]&#xff0c;将所有的采样、压缩均设置为 关闭。点击[另存为]&#xff0c;保存为 基金报告…

基于RKNN的嵌入式深度学习开发(2)

上一个章节我们介绍的RKNN模型的模型转换和模型的推理&#xff0c;这一章节我们将介绍模型的量化和评估部分。 2.3 RKNN模型的量化 量化就是将浮点转换为定点运算的过程&#xff0c;或者训练后由rknn来量化。量化模型使用较低精度&#xff08;如int8/uint8/int16&#xff09;保…

单一职责原则(设计模式)

目录 问题&#xff1a; 定义&#xff1a; 解决&#xff1a; 方式 1&#xff1a;使用策略模式 示例&#xff1a;用户管理 方式 2&#xff1a;使用装饰者模式 示例&#xff1a;用户操作 方式 3&#xff1a;使用责任链模式 示例&#xff1a;用户操作链 总结 推荐 问题&a…

Java 8 到 Java 17 主要新特性

Java 8 到 Java 17 是 Java 语言的多个重要版本&#xff0c;其中每个版本都引入了新的特性和改进。下面是 Java 8 到 Java 17 中主要的新特性概览。 Java 8 新特性 1. Lambda 表达式 Lambda 表达式是 Java 8 的一个重要特性&#xff0c;它使得 Java 支持函数式编程&#xff…