SpringBoot相关知识点

本文详细探讨了Spring框架中的IoC和DI概念,Spring容器的Bean加载时机,@Autowired的使用和原理,@Configuration的配置类与@Conditional的条件应用,以及SpringBoot自动配置的奥秘。从依赖注入到自动装配,为你揭示了Spring Boot核心组件的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. IOC和DI

首先,我们应该明确,IOC是一种思想,并不是Spring特有的,而是软件工程逐步发展的一种产物,是一种优秀的编程思想,之所以我们经常会把IOC理解成是Spring特有的东西,是因为Spring框架可以帮助我们很好的去实现IOC。IOC代表的是控制反转,控制的是什么?反转的是什么?控制的是一个对象的创建、初始化和销毁,没有使用IOC之前,我们使用一个对象,会主动的去创建对象,也就是在需要使用这个对象的地方去执行 A a = new A(); 类似这种语句,而使用了IOC之后,会把系统中所有需要使用到的对象交给IOC容器去托管,当我们的程序中需要使用到某个对象时,也不是我们直接去容器获取这个对象,而是容器会主动的给我们这个对象,也就是说IOC容器会自动给程序中需要对象的地方给你注入对象,这就是反转,把获取对象的行为由主动变为被动,并且把控制权交给了容器。

DI代表的是依赖注入,什么是依赖注入?我们都知道,对象与对象之间一般都会有依赖关系的,例如对象A依赖于对象B,对象B又依赖于对象C,举个例子,对象A中有个方法a,a方法的实现需要调用B对象的b方法,这就是依赖,引入IOC之前,我们会在A对象中new一个B对象,再通过B对象去调用b方法;引入IOC之后,我们都不会去主动创建对象了,都交给IOC容器去处理了,我们只需要在对象A中声明一个B对象的成员变量,加上xml配置或者相关注解,IOC容器就可以我们自动实现注入。例如下面这段代码,在MVC架构中,Controller层依赖Service层,我们只需要在Controller层声明一个Service层的变量,并加上@Autowired注解,当IOC容器启动时加载ApplicationController这个Bean到容器中时,会自动把ApplicationService这个Bean也注入进来。
请添加图片描述
依赖注入的实现方式有三种:构造函数注入、set方法注入以及接口注入;接口注入用的比较少,一般都是使用构造函数注入和set方法注入,另外还有一种字段注入,如上图所示的注入方式就是字段注入,这种注入方式不是规范的,可以看到@Autowired注解会给出警告,不建议我们使用这种方式,但这种方式却是使用最多的,因为它很方便。
请添加图片描述
以下是构造函数注入:
请添加图片描述
以下是set方法注入:
请添加图片描述

2. Spring容器加载Bean/创建对象的时机

默认情况下,程序启动的时候,Spring就会把程序中需要用到的Bean加载到IOC容器中,也就是会一次性创建很多的对象到IOC容器中。当然,还有一种方式,就是当需要使用到某个Bean的时候,再去创建这个Bean到容器中,这就是延迟加载,或者叫延迟实例化。

如果我们希望某个Bean不要过早的被加载到容器中,使用到的时候再去创建,可以给这个Bean加上@Lazy注解。
请添加图片描述
关于@Lazy注解实现延迟加载,需要注意一个细节,我们给某个Bean加上@Lazy注解之后,并不一定就能实现延迟加载,有可能程序在启动的时候,这个Bean还是会在一开始就被创建,会产生这种情况是因为这个Bean有可能在别处被引用到,也就是说别的Bean可能依赖了这个Bean,而依赖的这个Bean又没有打上@Lazy注解,那么程序启动的时候,创建这个依赖的Bean,也会把它所依赖的Bean一并注入,这就是导致@Lazy注解有时没有生效的原因。

举个例子,ApplicationController这个Bean依赖了ApplicationServiceImpl,我们只给ApplicationServiceImpl这个Bean加上@Lazy注解,是无法实现延迟加载的,需要ApplicationController这个Bean也加上@Lazy注解。
请添加图片描述
请添加图片描述
请添加图片描述

3. @Autowired注解

@Autowired注解的作用就是实现依赖注入,大部分情况下,我们使用@Autowired这个注解就可以满足我们的需求了,但是我们需要知道这个注解的约定是什么,才能更好的使用它。首先,@Autowired注解实现的是按照类型注入,补充一个点:为了满足ocp原则,使得代码更加灵活便于扩展,我们倡导面向接口编程。例如下面这个例子,ApplicationService是一个接口,我们加上了@Autowired注解,按照类型注入的原则,IOC容器会去找ApplicationService接口的实现类去进行依赖注入,如果这个接口只有一个实现类,那么没有问题,可以实现注入,如果这个接口有多个实现类,那么IOC容器会知道给我们注入哪一个实现类吗,显然是不知道的,但是@Autowired还有一种规则,就是根据类型匹配时,如果能匹配多个,会再根据Bean的名称去匹配,如果Bean的名称能匹配上,那么没有问题,可以实现注入;如果Bean的名称匹配不上,程序会报错,因为IOC容器不知道给我们注入哪一个ApplicationService接口的实现类。
请添加图片描述
举个例子,假设现在ApplicationService接口有两个实现类ApplicationServiceImpl1(Bean名称:applicationServiceImpl1)和ApplicationServiceImpl2(Bean名称:applicationServiceImpl2),如果@Autowired注解作用的代码是private ApplicationService applicationServiceImpl1;,那么注入的是ApplicationServiceImpl1这个Bean,如果@Autowired注解作用的代码是private ApplicationService applicationServiceImpl2;,那么注入的是ApplicationServiceImpl2这个Bean,如果@Autowired注解作用的代码是private ApplicationService applicationServiceImpl;,那么程序报错,IOC容器知道为我们注入哪个Bean。

如果涉及一个接口有多个实现类的情况,我们建议使用@Autowired注解搭配@Qualifier注解,@Qualifier注解的实现机制是按照Bean的名称进行注入。

例如下面这个例子,首先@Autowired注解会根据类型找到ApplicationService的实现类,@Qualifier注解再找到名称为applicationServiceImpl的Bean进行注入。
请添加图片描述
PS:上面我们讲的是@Autowired注解如果匹配到多个Bean不知道注入哪一个的时候会报错,还有一种情况,如果@Autowired注解匹配不到任何对应的Bean也会报错。

4. @Configuration配置类

一个类被加上了@Configuration注解,就表明这个类是一个配置类,@Configuration注解底层注解是@Component,也就是说,注解类也会被加载到IOC容器中,在注解类中,通常会声明很多方法,这些方法的返回值是一个对象,并且方法加上了@Bean注解,这表明会将这些方法返回的对象加载到IOC容器中。例如:
请添加图片描述
@Configuration注解是用来替换Bean的xml配置,可以把@Configuration看作标签,把@Bean注解看作标签

传统的xml配置是这样的:

<beans>
    <bean id = "people", class = "com.test.People">
    </bean>
    <bean id = "student", class = "com.test.Student">
    </bean>
</beans>

思考一个问题:一般来说,把一个Bean加载到IOC容器中,不是通过@Component注解就可以实现吗?况且@Configuration注解的底层注解还是@Component,那为什么还需要@Configuation+@Bean这套注解来加载某些Bean到IOC容器中呢?换言之,@Configuation+@Bean的真正作用是什么?

@Configuation+@Bean的作用就是可以将一些定制化的Bean(特殊的Bean)注入到IOC容器中,什么是定制化的Bean?举个例子,有一个People类,有两个属性name和age,我们希望把People类加载到IOC容器中,并且使得IOC容器的这个Bean的name属性值是小明,age属性值是18。如果只是在People类上加@Component注解,是无法实现我们这个定制化的Bean的,因为IOC容器在启动的时候,创建Bean,默认调用的都是某个类的无参构造函数(也存在调用带参构造函数的情况,但是这种情况使用带参构造函数,只是为了实现依赖注入,也就是构造函数注入,并非是给属性赋值),也就是说无法给我们的属性赋特殊的值,而使用@Configuation+@Bean就可以实现。例如:
请添加图片描述
请添加图片描述
通常来说,我们不会直接把属性值写死在代码里,而是通过读取配置文件的形式,虽然说@Configuration注解表明这个类已经是一个配置类了,可以充当配置文件来使用,但是对于一些变化的属性,我们都会定义在普通配置文件里面,例如application.properties。
请添加图片描述
请添加图片描述

5. @Conditional条件注解

@Conditional注解的作用是满足某个条件时,才会将这个Bean注入到IOC容器中。我们可以通过自定义一个Condition类的方式定制某个规则,当满足这个规则才会注入Bean,例如:
请添加图片描述
请添加图片描述
通常,我们直接使用一些成品条件注解就可以满足我们的需求了。常用成品条件注解如下:
请添加图片描述

6. SpringBoot的自动配置/自动装配

自动装配是SpringBoot最核心的东西,学习自动装配,我们首先应该了解自动装配是什么,做了哪些事情?其次需要明确自动装配存在的意义,也就是说自动装配的好处是什么?最后再去了解自动装配的原理。按照这个流程下来,才能更好的掌握自动装配这个知识点。

a. 自动装配做了什么事情? 就是SpringBoot会自动把一些第三方的库或者SDK需要使用到的很多Bean都自动帮我们加载到IOC容器中。

b. 自动装配的好处是什么? 基于IOC思想,任何程序中使用到的Bean都需要注入IOC容器中进行托管,也就是说,我们引用一些第三方的库/依赖,例如Mongodb、Redis、Hadoop,也是需要把这些第三方的库涉及的Bean都加载到IOC容器中的。如果没有自动装配,那么如何把第三方的库里面的很多Bean都加载到我们的IOC容器中来,给每一个相关的类打上@Component注解?不是这样的,我们所引用的第三方库一般都是以jar包的形式存在,并不是直接给源码。即便是可以通过打上@Component注解的方式,试想我们的工作量会增加多少,引用一个第三方库可能涉及很多的Bean,并且有些Bean又可能是比较复杂的,我们不可能手动的将他们一个个注入容器,而SpringBoot都帮我们做好了,这就是自动装配的好处。

c. 自动装配的原理

首先,在SpringBoot程序的主启动类上,有一个非常重要的注解:@SpringBootApplication,进入到这个注解,可以发现底层由三个注解组成(元注解除外)
请添加图片描述
@SpringBootConfiguration:表明SpringBoot程序的主启动类也是一个配置类

@ComponentScan:指定包扫描路径,程序启动时,会根据指定的包扫描路径去加载Bean

@EnableAutoConfiguration:最重要的一个注解,我们可以理解成这个注解的作用是开启自动装配

进入到@EnableAutoConfiguration,可以发现底层由两个注解组成(元注解除外)
请添加图片描述
我们主要研究@Import注解,这个注解有两种实现方式,第一种是指定一个或多个配置类,例如:@Import(TetsConfiguration.class) 那么IOC容器就会将这个配置类加载到容器中来,另一种实现方式是指定一个ImportSelector类的子类,例如@Import(AutoConfigurationImportSelector.class) 这个ImportSelector类的子类会去实现selectImports方法,该方法的返回值是一个字符串数组,代表要加载的配置类名称列表。

SpringBoot自动装配默认使用第二种方式,我们进到AutoConfigurationImportSelector这个类里面,发现它确实实现了selectImports方法。通过这个方法,SpringBoot就可以知道我们需要自动装配哪些配置类
请添加图片描述
我们再进入getAutoConfigurationEntry方法,这个方法里面有一个核心方法getCandidateConfigurations,翻译过来就是获取候选的配置类
请添加图片描述
getCandidateConfigurations方法实现逻辑就是:会去读取某一个配置文件,根据这个配置文件的值返回自动装配要装配的配置类名称列表
请添加图片描述
我们进入到SpringFactoriesLoader类,可以知道getCandidateConfigurations读取的配置文件就是META-INF下面的spring.factories
请添加图片描述
spring.factories的位置
请添加图片描述
spring.factories配置文件主要就声明了很多自动配置类的名称,截取部分如下:
请添加图片描述
总结一下,我们一路追踪源码下来,无非确定了一件事,SpringBoot自动装配时装配了哪些配置类,这些配置类的定义都存在于spring.factories配置文件中。

另外,我们需要明确,SpringBoot自动装配时也不是把所有自动配置类定义的Bean都加载到IOC容器中,因为这些Bean大部分都会加上条件注解,需要满足一定的条件才会被加载,例如,我们进入某一个自动配置类看一下
请添加图片描述

学习尚硅谷视频整理的文档 Spring Boot 1 1 Spring Boot入门 4 1.1 简介 4 1.2 微服务(martin fowler发表了一篇文章) 5 1.3 环境约束 7 1.4 第一个Spring Boot项目(jar):HelloWorld 8 1.5 入门案例详解 11 1.5.1 POM文件 11 1.5.2 主程序类,主入口类 12 1.6 使用Spring Initializer向导快速创建Spring Boot 16 2 Spring Boot配置 18 2.1 配置文件 18 2.2 YML语法 19 2.3 YML配置文件值获取 21 2.4 properties配置文件乱码问题 24 2.5 @ConfigurationProperties与@Value的区别 25 2.6 配置@PropertySource、@ImportResource、@Bean 27 2.7 配置文件占位符 30 2.8 Profile多环境支持 31 2.9 配置文件的加载位置 33 2.10 外部配置加载顺序 36 2.11 自动配置原理 37 2.12 @Conditional派生注解 41 3 Spring Boot与日志 42 3.1 日志框架分类和选择 42 3.2 SLF4j使用 43 3.3 其他日志框架统一转换成slf4j+logback 44 3.4 Spring Boot日志使用 45 3.5 Spring Boot默认配置 47 3.6 指定日志文件和日志Profile功能 52 3.7 切换日志框架(不使用SLF4j+LogBack) 54 4 Spring Boot与Web开发 55 4.1 Web开发简介 55 4.2 静态资源映射规则 56 4.3 引入Thymeleaf 60 4.4 Thymeleaf语法 61 4.5 SpringMVC自动配置原理 67 4.6 SpringBoot扩展与全面接管 70 4.7 如何修改SpringBoot的默认配置 72 4.8 【实验】CRUD操作 73 4.8.1 默认访问首页 73 4.8.2 登录页面国际化 74 4.8.3 登录 80 4.8.4 拦截器进行登录检查 81 4.8.5 实验要求(没按要求做,不想改了!) 82 4.8.6 CRUD-员工列表 83 4.8.7 CRUD-员工修改 86 4.8.8 CRUD-员工添加 87 4.8.9 CRUD-员工删除 88 4.9 错误处理原理&错误页面定制 90 4.10 配置嵌入式Servlet容器(springboot 1.50版本) 97 4.10.1 如何定制和修改Servelt容器的相关配置 97 4.10.2 注册servlet三大组件【servlet,filter,listener】 98 4.10.3 替换为其他嵌入式容器 102 4.10.4 嵌入式servlet容器自动配置原理 103 4.10.5 嵌入式servlet容器启动原理 103 4.11 使用外置的Servlet容器 104 4.11.1 步骤 104 4.11.2 原理 107 5 Spring Boot与Docker(虚拟化容器技术) 110 5.1 简介 110 5.2 核心概念 111 5.3 安装Docker 112 5.4 Docker常用命令&操作 113 5.5 安装MySQL示例 114 6 Spring Boot与数据访问 115 6.1 JDBC 115 6.1.1 实现 115 6.1.2 自动配置原理 116 6.2 整合Durid数据源 117 6.3 整合Mybatis 122 6.3.1 注解版 123 6.3.2 配置文件版 124 6.4 整合SpringData JPA 125 6.4.1 SpringData简介 125 6.4.2 整合 126 7 Spring Boot启动配置原理 128 7.1 启动流程(Springboot 1.50版本) 128 7.1.1 创建SpringApplication对象 129 7.1.2 运行run方法 130 7.1.3 编写事件监听机制 132 8 Spring Boot自定义starters 136 8.1 概述 136 8.2 步骤 137 9 更多Springboot整合示例 144 10 Spring Boot与缓存 145 10.1 JSR107缓存规范 145 10.2 Spring的缓存抽象 146 10.2.1 基本概念 146 10.2.2 整合项目 146 10.2.3 CacheEnable注解 148 10.2.4 Cache注解 150 10.3 整合redis 154 10.3.1 在Docker上安装redis 154 10.3.2 Redis的Template 154 10.3.3 整合(百度) 155
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值