SpringBoot 学习笔记

文章目录

  • 一、IoC
  • 二、AOP
  • 三、bean
    • 3.1 bean 生命周期
    • 3.2 三种依赖注入方式
    • 3.3 bean 线程安全
  • 四、SpringMVC
  • 五、常用注解
    • 5.1 @Scope
    • 5.2 @PostConstruct 和 @PreDestroy
    • 5.3 @Component 和 @Bean
    • 5.4 @Autowired 和 @Resource
  • 六、基于 ApplicationContextAware 实现工厂模式
  • 七、事务失效
  • 八、三级缓存与循环依赖


一、IoC

IoC(Inversion of Control)控制反转:控制反转是一种设计思想,它将组件的创建和管理交给容器,从而降低组件之间的耦合度。控制是指实例化以及管理对象的权力,反转是指将控制权交给 IoC 容器。IoC 容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在 IoC 容器中统称为 bean。


二、AOP

面向切面编程的核心思想就是将横切关注点从核心业务逻辑中分离出来,形成一个个的切面。横切关注点指的是一些分散在多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等操作),如果我们在每个类或对象中都重复实现这些行为,那么会导致代码的冗余、复杂和难以维护。

Spring AOP 基于动态代理实现。当目标对象实现了某个接口时,Spring AOP 会使用 JDK 动态代理来生成对应接口的代理对象。对于没有实现接口的对象,Spring AOP 会使用 CGLIB 生成一个目标对象的子类作为代理对象。在运行时,代理类会替代原始目标类来执行方法调用,而代理类中的功能是基于切面类定义和实现的。

AOP 中的核心概念:

  • 目标对象:被代理或操作的对象。
  • 连接点:目标对象中定义的所有可被 AOP 控制的方法均为连接点。
  • 切入点:被实际增强的连接点,具体范围由切入点表达式控制。
  • 通知:拦截到连接点之后要执行的增强逻辑,比如一些共性功能。
  • 切面:切入点 + 通知。

切入点表达式用来描述切入点方法的表达式,主要用来决定目标对象中的哪些连接点需要加入通知,主要包括使用 execution() 根据方法的签名来匹配和使用 @annotation() 根据注解匹配。


三、bean

3.1 bean 生命周期

  • 初始化容器
    • 通过反射实例化 bean,创建对象。
    • 执行构造方法。
    • 如果实现了 Aware 相关依赖,如 ApplicationContextAware,执行对应方法。
    • 属性赋值与依赖注入,解决循环依赖。
    • 执行 @PostConstruct 注解标识的方法。
    • 如果项目中实现了 BeanPostProcessor 接口,将当前类作为参数执行自定义的 postProcessBeforeInitialization 方法。
    • 如果实现了 InitializingBean 接口,执行 afterPropertiesSet 方法。
    • 如果配置了自定义的 init-method,即 @Bean(initMethod = ""),执行对应方法。
    • 如果项目中实现了 BeanPostProcessor 接口,将当前类作为参数执行自定义的 postProcessAfterInitialization 方法。
  • 获取并使用 bean
  • 销毁容器
    • 如果配置了自定义的 destroy-method,即 @Bean(destroyMethod = ""),执行对应方法。
    • 如果实现了 DisposableBean 接口,执行 destory() 方法。

3.2 三种依赖注入方式

  • 字段注入:实现简单,但是存在注入对象不能用 final 修饰、难以进行单元测试等问题,因此并不推荐使用字段注入。
  • 构造器注入:唯一一个注入对象可以使用 final 修饰的注入方法,同时能够检测循环依赖。但是,当一个类有很多依赖项时构造函数的参数列表可能会变得很长。此外,如果一个类有可选的依赖项,可能需要创建多个构造函数重载,灵活性较低。
  • setter 方法注入:灵活性较高,不过注入对象也不能用 final 修饰。

总的使用原则是:强制的依赖就用构造器注入,可选、可变的依赖就用 setter 注入。

3.3 bean 线程安全

bean 是否线程安全,取决于其作用域和状态。

以最常用的两种作用域 prototypesingleton 为例:

  • prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。
  • singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题,具体要看 bean 是否有状态,如果是无状态 bean,那就不存在线程安全问题,如果是有状态 bean,那就存在线程安全问题。这里的有无状态指的是对于成员变量,除了查询以外,是否还会对其进行修改。

对于有状态单例 bean 的线程安全问题,有以下几种解决办法:

  • 在 bean 中尽量避免定义可变的成员变量。
  • 通过 ThreadLocal 或互斥锁控制成员变量的修改和访问。
  • 采用 prototype 作用域。

四、SpringMVC

SpringMVC 技术与 Servlet 技术功能等同,均属于表现层开发技术。

在这里插入图片描述


五、常用注解

5.1 @Scope

声明 bean 的作用域,常见的有以下四种:

  • singleton:Spring 中默认的作用域,IoC 容器中只有唯一的 bean 实例,这意味着所有客户端共享相同的 bean 实例。
  • prototype:每次获取都会创建一个新的 bean 实例,这意味着每个客户端都拥有自己的 bean 实例。
  • request:bean 的生命周期与 HTTP 请求的生命周期相对应,每个 HTTP 请求都会创建一个新的 bean 实例,该 bean 仅在该请求内可见。
  • session:每一次来自新 session 的 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 session 内有效。

使用示例:

@Component
@Scope("singleton")
public class Solution {
}

5.2 @PostConstruct 和 @PreDestroy

@PostConstruct@PreDestroy 并非由 Spring 提供,而是 Java 自带的注解。@PostConstruct 会在依赖注入完成后被自动调用,并且只会被调用一次,用于完成一些初始化操作。而 @PreDestroy 则会在容器销毁 bean 的时候回调执行,用于完成相关的销毁操作。

bean 初始化过程中的执行顺序为:

Constructor(构造方法)-> @Autowired(依赖注入)-> @PostConstruct(初始化方法)

使用示例:

@Component
public class Solution {@PostConstructvoid init() {System.out.println("init...");}@PreDestroyvoid destroy() {System.out.println("destroy...");}
}

5.3 @Component 和 @Bean

  • @Component 注解作用于类,标识这是一个 bean,而 @Bean 注解作用于方法,通常方法体中包含产生 bean 的逻辑。
  • @Component 通常是通过类路径扫描来自动侦测 bean 并装配到 Spring 容器中,可以使用 @ComponentScan 定义要扫描的路径,而 @Bean 通常指定了这个方法的返回值将被注册为一个 bean。
  • @Bean 注解比 @Component 注解的自定义性更强,可以在方法内部实现更复杂的 bean 创建和初始化逻辑,同时第三方依赖中的 bean 只能通过 @Bean 声明(因为第三方的 bean 是只读的,没法加 @Component 注解)。

5.4 @Autowired 和 @Resource

  • @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。
  • @Autowired 默认的注入方式是根据类型匹配,@Resource 默认的注入方式是根据名称匹配。
  • 当一个接口存在多个实现类的情况下,@Autowired@Resource 除了都能够通过名称匹配到对应的 bean 以外,@Autowired 还可以通过 @Qualifier 注解来显式指定,@Resource 则可以通过 name 属性来显式指定。
  • @Autowired 支持在构造函数、方法、字段和参数上使用,@Resource 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。

六、基于 ApplicationContextAware 实现工厂模式

抽象产品:

package atreus.ink.log;import org.springframework.stereotype.Component;@Component
public abstract class AbstractLog {protected abstract String getName();protected void doLog() {System.out.println("atreus.ink.log.AbstractLog#doLog");}
}

具体产品:

package atreus.ink.log;import org.springframework.stereotype.Component;@Component
public class JavaLog extends AbstractLog {@Overridepublic String getName() {return "JavaLog";}@Overridepublic void doLog() {super.doLog();System.out.println("atreus.ink.log.JavaLog#doLog");}
}
package atreus.ink.log;import org.springframework.stereotype.Component;@Component
public class CppLog extends AbstractLog {@Overridepublic String getName() {return "CppLog";}@Overridepublic void doLog() {super.doLog();System.out.println("atreus.ink.log.CppLog#doLog");}
}

工厂:

package atreus.ink.log;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;@Component
public class LogFactory implements ApplicationContextAware {private static final Map<String, AbstractLog> map = new HashMap<>();@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {Map<String, AbstractLog> beansOfType = applicationContext.getBeansOfType(AbstractLog.class);beansOfType.forEach((k, v) -> map.put(v.getName(), v));}public static AbstractLog getLog(String name) {return map.get(name);}
}

测试:

package atreus.ink.log;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class LogFactoryTest {@Testvoid getLogTest() {AbstractLog javaLog = LogFactory.getLog("JavaLog");javaLog.doLog();System.out.println("----------");AbstractLog cppLog = LogFactory.getLog("CppLog");cppLog.doLog();Assertions.assertTrue(true);}
}
atreus.ink.log.AbstractLog#doLog
atreus.ink.log.JavaLog#doLog
----------
atreus.ink.log.AbstractLog#doLog
atreus.ink.log.CppLog#doLog

七、事务失效

Spring 事务失效场景:

  • 数据库引擎不支持事务,比如 MySQL 的 MyISAM 引擎就不支持事务。
  • 使用事务的类没有交由 Spring 管理,需要对事务类加 @Repository@Service@Controller@Component 注解。
  • 方法不是 public 的,@Transactional 只能用于 public 的方法上,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
  • 自调用问题,由于 Spring 事务管理基于动态代理,而代理对象无法拦截类的内部调用,如果确实需要自调用,可以使用通过 AopContext.currentProxy() 手动获取代理对象或者在内部注入自己对应的 bean。
  • 异常在函数内部被处理,只有将异常抛出 Spring 才能检测到异常并进行回滚。

八、三级缓存与循环依赖

Spring 三级缓存(以 singleton 作用域为例):

  • 一级缓存singletonObjects,缓存已经完成实例化和初始化的成品 bean。
  • 二级缓存earlySingletonObjects,缓存已经被其他对象引用的半成品 bean,这些 bean 被提前暴露。
  • 三级缓存singletonFactories,缓存未被其他对象引用的半成品 bean 的工厂,使用时通过这些工厂创建 bean。

三级缓存解决循环依赖(假设 A 和 B 之间存在循环依赖):

  • 实例化 A,此时 A 还未完成属性赋值和初始化,A 只是一个半成品。
  • 为 A 创建一个 bean 工厂,并放入到 singletonFactories 中。
  • 发现 A 需要注入 B 对象,但是一、二、三级缓存均未发现对象 B。实例化 B,此时 B 还未完成属性赋值和初始化,B 只是一个半成品。
  • 为 B 创建一个 bean 工厂,并放入到 singletonFactories 中。
  • 发现 B 需要注入 A 对象,此时在三级缓存中发现了对象 A,从三级缓存中通过 bean 工厂得到对象 A,并将对象 A 移入二级缓存。
  • 将对象 A 注入到对象 B 中。
  • 对象 B 完成属性填充,执行初始化方法,移入一级缓存,此时对象 B 已经是一个成品。
  • 对象 A 得到对象 B,将对象 B 注入到对象 A 中。
  • 对象 A 完成属性填充,执行初始化方法,移入一级缓存,此时对象 A 也已经是一个成品。

两级缓存也能解决循环依赖,为什么还需要第三级缓存,第三级缓存又为什么缓存 bean 工厂而不是 bean呢?

其实第三级缓存的主要目的是延迟代理对象的创建,如果没有循环依赖的话,第三级缓存可以将代理对象的创建延迟到初始化完成之后,不需要提前

具体来说,如果创建的 bean 是有代理的,那么注入的就应该是代理 bean,而不是原始的 bean。按照 Spring 的设计原则,Spring 会在完成属性赋值与依赖注入并且执行完初始化方法之后再为其创建代理

对于三级缓存来说,如果需要创建代理,在没有循环依赖的情况下,Spring 首先会为已经实例化的 bean 在三级缓存中创建一个工厂,然后进行属性赋值、依赖注入和初始化,最后创建代理放入一级缓存,也即在完成初始化等操作后创建代理类。如果出现了循环依赖,Spring 会通过三级缓存中工厂类的方法去提前创建代理对象,放入二级缓存,在完成初始化等操作后再放入一级缓存,这种情况下就是先创建代理再初始化。

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

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

相关文章

[AutoSar]BSW_Com03 DBC详解 (一)

目录 关键词平台说明一、DBC 定义1.1 相关工具 二、主要组成部分介绍2.1 Networks2.2 ECUs2.3 Network nodes2.4 messages2.5 signal2.6 Value Tables 三、主要组成部分关系图 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueOSautosar OSautosar厂商vector &am…

推荐一个 Obsidian 的 ChatGPT 插件

源码地址&#xff1a;https://github.com/nhaouari/obsidian-textgenerator-plugin Text Generator 是目前我使用过的最好的 Obsidian 中的 ChatGPT 功能插件。它旨在智能生成内容&#xff0c;以便轻松记笔记。它不仅可以在 Obsidian 中直接使用 ChatGPT&#xff0c;还提供了优…

二叉树高频题目(不含树形DP)

二叉树高频题 二叉树的层序遍历 . - 力扣&#xff08;LeetCode&#xff09; 按点弹出 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>>ans;if(root!nullptr){queue<TreeNode*>q;unordered_map&…

音视频技术-电脑连接调音台时交流声的产生与消除

当电脑&#xff08;笔记本/台式机&#xff09;声卡通过音频线与调音台&#xff08;或扩音机&#xff09;连接时&#xff0c;能听到“交流声”。有时很轻微&#xff0c;有时很明显&#xff0c;甚至干扰正常的演讲或发言。 很多时候&#xff0c;我们在台上演讲时&#xff0c;都会…

Centos7.9环境源码编译安装ffmpeg6.x

1.官网ffmpeg下载源码 https://ffmpeg.org/download.html#build-windows 2.未安装x264库则先安装配置 可以先查询x264库: whereis libx264 安装编译工具和依赖库&#xff1a; sudo yum install gcc make cmake mercurial git yasm pkgconfig autoconf automake libtool sudo…

UE4 材质多张图片拼接成一张图片(此处用2×2拼接)

UE4 材质多张图片拼接成一张图片&#xff08;此处用22拼接&#xff09; //TexCoord,TextureA,TextureB,TextureC,TextureDfloat3 ReturnTexture TextureA; if(TexCoord.x < 0.5 && TexCoord.y < 0.5) {ReturnTexture TextureA; } else if(TexCoord.x > 0.5…

力扣1290. 二进制链表转整数

Problem: 1290. 二进制链表转整数 文章目录 题目描述思路复杂度Code 题目描述 思路 1.记录一个变量res初始化为0&#xff0c;指针p指向链表头&#xff1b; 2.循环每次res res * 2 p -> val;p p -> next;&#xff08;充分利用二进制数的特性&#xff1b;其中利用指针先…

VMware使用虚拟机,开启时报错:无法连接虚拟设备 0:0,因为主机上没有相应的设备。——解决方法

检查虚拟机配置文件并确保物理设备已正确连接。 操作&#xff1a; 选中虚拟机&#xff0c;打开设置&#xff0c;点击CD/DVD。在连接处选择使用ISO镜像文件

.top域名解析超过72小时ping不通

域名作为网络上网站的唯一标识&#xff0c;它可以是可视的文本字符串&#xff0c;也可以是IP地址&#xff0c;它提供了一种方便的方式来访问网站&#xff0c;而不用担心网址的改变。域名可以分为不同的类型&#xff0c;如顶级域名(TLD)、国家域名(ccTLD)、通用域名(gTLD)等。因…

怎样知道员工上班浏览了哪些网页

在数字化时代&#xff0c;员工在工作时间内上网浏览网页已经成为常态。然而&#xff0c;这也为企业带来了信息安全和工作效率的隐患。为了解决这个问题&#xff0c;许多企业开始使用域智盾这样的专业软件来监控员工的上网行为。 一、域智盾软件简介 域智盾是一款功能强大的企业…

操作系统--设备管理

一、设备控制器 我们的电脑设备可以接非常多的输入输出设备&#xff0c;比如键盘、鼠标、显示器、网卡、硬盘、打印机、音响等等&#xff0c;每个设备的用法和功能都不同。为了屏蔽设备之间的差异&#xff0c;每个设备都有一个叫设备控制器&#xff08;Device Control&#xf…

电子书推荐|IT 基础架构团队的 K8s 管理(含最新性能评测)

越来越多的企业采用 Kubernetes 支持应用的快速开发与交付&#xff0c;Kubernetes 的部署与管理任务也逐渐向 IT 基础架构团队倾斜。尤其是对于习惯了传统虚拟化环境的基础架构工程师&#xff0c;容器环境的管理方式往往会带来诸多困扰&#xff1a; Kubernetes 使用门槛高&…

期权无风险套利策略[6]—看跌期权价格波动区间套利

看跌期权价格波动区间定义 根据美式看涨和看跌期权的均衡公式&#xff0c;在无现金股利的发放下&#xff1a; &#xff08;1&#xff09;美式看跌期权的价格&#xff08;P&#xff09;一定不会超过同等特征美式看涨期权的价格 &#xff08;C&#xff09;&#xff0c;加上执行…

React18源码: Fiber树的初次创建过程图文详解

fiber树构造&#xff08;初次创建&#xff09; fiber树构造的2种情况&#xff1a; 1.初次创建 在React应用首次启动时&#xff0c;界面还没有渲染此时并不会进入对比过程&#xff0c;相当于直接构造一棵全新的树 2.对比更新 React应用启动后&#xff0c;界面已经渲染如果再次发…

面试经典150题【31-40】

文章目录 面试经典150题【31-40】76.最小覆盖字串36.有效的数独54.螺旋矩阵48.旋转图像73.矩阵置零289.生命游戏383.赎金信205.同构字符串290.单词规律242.有效的字母异位词 面试经典150题【31-40】 76.最小覆盖字串 基本思路很简单&#xff0c;就是先移动右边到合适位置。再移…

面对耦合度过高的第三方对接

今天接到一个需求&#xff0c;原型图如下&#xff1a;抖音数据&#xff08;给的是数据占比&#xff09;&#xff0c;要求根据性别&#xff0c;年龄&#xff0c;类目&#xff0c;地域&#xff0c;消费偏好&#xff0c;客单价筛选后排序。 1、性别给的示例&#xff1a;{ "…

Rem 自适应原理与应用

前言 移动端适配有很多方案&#xff0c;这篇文章将根据 rem 自适应的原理进行讲解&#xff0c;接下来跟着作者的思路一起来看看吧&#xff01; 原理 在搞清楚 rem 适配之前&#xff0c;我们先来了解一下什么是 rem&#xff1f; rem 是一种相对长度单位&#xff0c;它相对于根…

51单片机学习(5)-----蜂鸣器的介绍与使用

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 蜂鸣器的介绍 1.蜂鸣器介绍 2.压电式蜂鸣器 &#xff08;无源…

LeetCode 第一题: 两数之和

文章目录 第一题: 两数之和题目描述示例 解题思路Go语言实现 - 一遍哈希表法C实现算法分析 排序和双指针法Go语言实现 - 排序和双指针法C算法分析 暴力法Go语言实现 - 暴力法C算法分析 二分搜索法Go语言实现 - 二分搜索法C算法分析 第一题: 两数之和 ‍ 题目描述 给定一个整…

6.2 认证授权模块 - 继承JWT、网关认证

认证授权模块 - 继承JWT、网关认证 文章目录 认证授权模块 - 继承JWT、网关认证一、JWT1.1 普通令牌1.2 JWT 令牌介绍1.3 测试生成JWT 令牌1.3.1 TokenConfig1.3.2 授权服务器配置 AuthorizationServer1.3.3 WebSecurityConfig 安全管理配置1.3.4 返回信息 1.4 资源服务集成 JW…