SpringBoot 学习笔记

news2025/1/14 18:14:15

文章目录

  • 一、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 {
    @PostConstruct
    void init() {
        System.out.println("init...");
    }
    
    @PreDestroy
    void 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 {
    
    @Override
    public String getName() {
        return "JavaLog";
    }
    
    @Override
    public 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 {
    
    @Override
    public String getName() {
        return "CppLog";
    }
    
    @Override
    public 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<>();
    
    @Override
    public 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 {
    
    @Test
    void 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.coloradmin.cn/o/1470920.html

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

相关文章

Cubase学习:Cubase 12常用快捷键

按键盘上的上下箭头就可以让选中的音符向上或向下移动 数字0键: 停止 Ctrl+数字 0 键: 新建视图层 Alt+数字0 键: 重新设置视图层 小数点键: 播放指针回零点 数字1 键: 左定位指针 数字 2 键: 右定位指针 数字3 键--数字9键: 分别控制 3--9 的7个定位标志 Alt+数字1 键--数字9键…

BoomWorks使用wxWidgets+CodeBlocks+GCC开发的软件合集

♦️ 定时执行专家&#xff08;TimingExecutor&#xff09; V7.0 《定时执行专家》是一款制作精良、功能强大、毫秒精度、专业级的定时任务执行软件。软件具有 25 种【任务类型】、12 种【触发器】触发方式&#xff0c;并且全面支持界面化【Cron表达式】设置。软件采用多线程并…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 2月26日,星期一

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年2月26日 星期一 农历正月十七 1、 气象台&#xff1a;3月初之前南方大部将维持阴雨雪天气。 2、 据海关统计&#xff0c;京津冀协同发展十年成效显著&#xff0c;外贸总量跨两个万亿台阶。 3、 2024年研考初试成绩今天起…

C++入门全集(2):类与对象【上】

目录 一、前言 二、struct在C中的变化 三、类的定义 四、类的访问限定符 五、封装 六、类的实例化 七、类对象模型 7.1 如何计算类对象的大小 7.2 类对象的存储方式 八、this指针 8.1 this指针的用途 8.2 this指针的特性 一、前言 C语言是一种面向过程的语言&…

Folx Pro Mac中文p破解版如何使用?为您带来Folx Pro 详细使用教程!

​ Folx pro 5 中文版是mac上一款功能强大的老牌加速下载软件&#xff0c;新版本的Folx pro整体界面非常的简洁和漂亮&#xff0c;具有非常好用的分类管理功能&#xff0c;支持高速下载、定时下载、速度控制、iTunes集成等功能。Folx pro兼容主流的浏览器&#xff0c;不但可以下…

什么是媒体发稿?发稿媒体分类及发稿流程

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体发稿是一种企业推广和宣传的手段&#xff0c;通过媒体渠道传递企业信息和形象。 媒体发稿的含义在于&#xff0c;当企业有新闻、事件或其他消息需要对外公布时&#xff0c;可以选择…

氢氟酸在晶圆厂中的应用与防护

氢氟酸&#xff0c;这种剧毒液体竟然会被用在晶圆的生产中&#xff0c;这个可能让你匪夷所思。实际上&#xff0c;氢氟酸这种看似普通的清澈液体&#xff0c;实则在芯片生产中扮演了至关重要的角色。 氢氟酸的性质&#xff1f; 氢氟酸&#xff08;Hydrofluoric acid&#xff0c…

【VSCode】解决VSCode远程连接问题:远程主机可能不符合 glibc 和 libstdc++

今天用VSCode进行ssh连接时&#xff0c;提示“远程主机可能不符合 glibc 和 libstdc VSCode 服务器的先决条件”。查了一下发现这个问题主要是由于VSCode在一月份发布的最新版本v1.86中要求远程主机 glibc>2.28导致的&#xff0c;所以ssh连接Ubuntu 18.04的时候就会提示这个…

AI之T2I:Stable Diffusion 3的简介、安装和使用方法、案例应用之详细攻略

AI之T2I&#xff1a;Stable Diffusion 3的简介、安装和使用方法、案例应用之详细攻略 目录 Stable Diffusion 3的简介 1、效果测试 官方demo 网友提供 Stable Diffusion 3的安装和使用方法 1、安装 2、使用方法 Stable Diffusion 3的案例应用 1、基础案例 Stable Diff…

jmeter 测试数据库的详细方法

当前版本&#xff1a; jmeter 5.6.3mysql 5.7.39 简介 JMeter是一个功能强大的开源性能测试工具&#xff0c;它被广泛用于测试Web应用程序、Web服务、API、数据库以及其他类型的应用程序。支持测试数据库的性能和可靠性&#xff0c;可以模拟多个并发用户对数据库执行各种不同类…

react中修改state中的值无效?

// 初始化state state {personArr:[{name:张三,id:1},{name:李四,id:2},{name:王五,id:3}] }componentDidMount(){const newName 赵六const indexUpdate 1const newArr this.state.personArr.map((item,index)>{if(indexUpdate index){return {...item,name:newName}}e…

二阶低通滤波器(博途PLC SCL源代码)

在学习滤波器之前我们先了解下截止频率的准确定义,周期正弦信号经过传递函数后的输出信号,其幅值衰减-3dB时对应的频率。-3dB的含义是幅值衰减为原来的约0.707。更多滤波器信号处理相关内容请参看下面文章链接: 1、PLC一阶低通滤波器 https://rxxw-control.blog.csdn.net/…

174基于matlab的雷达数字信号处理

基于matlab的雷达数字信号处理。该程序具备对雷达目标回波的处理能力&#xff0c;能够从噪声中将目标检测出来&#xff0c;并提取目标的距离、速度、角度信息。有相应的试验文档。程序已调通&#xff0c;可直接运行。 174 雷达数字信号处理 目标检测出来 (xiaohongshu.com)

java数据结构与算法刷题-----LeetCode700. 二叉搜索树中的搜索

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 法一&#xff1a;递归2. 法二&#xff1a;迭代法 解题思路:时…

Qt RGB三色灯上位机

今天突发奇想做一个Qt RGB三色灯上位机&#xff0c;刚好手上有一个RGB三色灯和arduion开发板。 我是想实现一个颜色选择器界面然后鼠标点击颜色区域就可以发出rgb的值&#xff0c;然后把这个值通过串口线发送给arduion,arduion再解析出数据发送给RGB三色灯。 实现界面如下&…

STM32单片机基本原理与应用(九)

SDIO/SD卡实验 实验内容 将SD卡插入实训平台并烧写程序&#xff0c;开机后TFTLCD屏幕上会显示是否成功初始化SD卡并显示SD卡容量。 电路原理图 实验原理 SD卡的通信方式有两种&#xff1a;SPI和SDIO。SD卡有五种寄存器&#xff0c;如下表 SD 卡的指令由 6 个字节组成&…

Programming Abstractions in C阅读笔记:p293-p302

《Programming Abstractions in C》学习第73天&#xff0c;p293-p302总结&#xff0c;总计10页。 一、技术总结 1.时间复杂度 (1)quadratic time(二次时间) p293, Algorithms like selection sort that exhibit O(N^2) performance are said to run in quadratic time。 2…

Go Run - Go 语言中的简洁指令

原文&#xff1a;breadchris - 2024.02.21 也许听起来有些傻&#xff0c;但go run是我最喜欢的 Go 语言特性。想要运行你的代码&#xff1f;只需go run main.go。它是如此简单&#xff0c;我可以告诉母亲这个命令&#xff0c;她会立即理解。就像 Go 语言的大部分功能一样&…

软件运维维保方案-套用文档

软件运维维保方案 项目情况 1.1 项目背景 简述项目的来源、目的和重要性。 说明项目的规模、预算和预期目标。 1.2 项目现状 分析当前系统/软件的运行状态、存在的问题和潜在风险。 提供最近一次的维护报告或相关统计数据。服务简述 2.1 服务内容 明确运维服务的具体内容&…

当我拥有1PB的磁盘

在开始之前可以先看一下我的有关文件传输协议的博文。 因为今天会涉及到WebDAV文件传输协议。 未来会有关于个人NAS系统和软路由的相关文章大家可以关注一下。 原文地址&#xff1a;当我拥有1PB的磁盘 - Pleasure的博客 下面是正文内容&#xff1a; 前言 话不多说直接看效…