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.rhkb.cn/news/264456.html

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

相关文章

【了解机器学习的定义与发展历程】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱 简述概要 了解机器学习的定义与发展历程 知识图谱 机器学习&#xff08;Machine Learning&#xff0c;ML&#xff09;是一门跨学科的学科&#xff0c;它使用计算机模拟或实现人类学习行为&#xff0c;通…

设计模式: 策略模式

文章目录 一、什么是策略模式二、策略模式结构三、使用场景案例分析1、使用场景2、案例分析&#xff08;1&#xff09;消除条件分支 一、什么是策略模式 策略模式是一种行为型设计模式&#xff0c;它允许定义一组算法&#xff0c;并将每个算法封装在独立的类中&#xff0c;使它…

解决内嵌帆软报表出现重定向问题

最近收到反馈&#xff0c;某些程序的前端通过iframe标签内嵌finebi帆软报表时&#xff0c;出现一系列问题。 问题1: 如下图所示&#xff0c;单点登录(单点登录地址schema是https)后service地址的schema协议是http, 浏览器内核的安全测试不允许http访问https。 解决方案&#xf…

【C进阶】顺序表详解

文章目录 &#x1f4dd;线性表的概念&#x1f320; 顺序表&#x1f309;顺序表的概念 &#x1f320;声明--接口&#x1f309;启动&#x1f320;初始化&#x1f309;扩容&#x1f320;尾插&#x1f309; 打印&#x1f320;销毁&#x1f309; 尾删&#x1f320;头插&#x1f309;…

matlab 线性四分之一车体模型

1、内容简介 略 57-可以交流、咨询、答疑 路面采用公式积分来获得&#xff0c;计算了车体位移、非悬架位移、动载荷等参数 2、内容说明 略 3、仿真分析 略 线性四分之一车体模型_哔哩哔哩_bilibili 4、参考论文 略

Redis高并发分布锁实战

Redis高并发分布锁实战 问题场景 场景一: 没有捕获异常 // 仅仅加锁 // 读取 stock15 Boolean ret stringRedisTemplate.opsForValue().setIfAbsent("lock_key", "1"); // jedis.setnx(k,v) // TODO 业务代码 stock-- stringRedisTemplate.delete(&quo…

php脚本输出中文在浏览器中显示乱码

问题说明 这个问题一般出现在较低版本的php中&#xff0c;原因是php和浏览器的字符解析方式不对应 &#xff0c;导致中文字符被错误解析成乱码 &#xff08;注&#xff0c;此处的php版本任意切换是依赖于小皮面板&#xff08;phpstudy&#xff09;实现的&#xff0c;感兴趣可以…

Docker容器故障排查与解决方案

Docker是一种相对使用较简单的容器&#xff0c;我们可以通过以下几种方式获取信息&#xff1a; 1、通过docker run执行命令&#xff0c;或许返回信息 2、通过docker logs 去获取日志&#xff0c;做有针对性的筛选 3、通过systemctl status docker查看docker服务状态 4、通过…

【牛牛送书 | 第四期】《高效使用Redis:一书学透数据存储与高可用集群》带你快速学习使用Redis

前言&#xff1a; 当今互联网技术日新月异&#xff0c;随着数据量的爆炸式增长&#xff0c;如何高效地存储和管理数据成为了每个公司都必须面对的挑战。与此同时&#xff0c;用户对于应用程序的响应速度和稳定性要求也越来越高。在这个背景下&#xff0c;Redis 作为一个…

【lv14 day10内核模块参数传递和依赖】

一、模块传参 module_param(name,type,perm);//将指定的全局变量设置成模块参数 /* name:全局变量名 type&#xff1a; 使用符号 实际类型 传参方式 bool bool insmod xxx.ko 变量名0 或 1 invbool bool insmod xxx.ko 变量名0 或 1 charp char * insmod xxx.ko 变量名“字符串…

解析Hadoop三大核心组件:HDFS、MapReduce和YARN

目录 HadoopHadoop的优势 Hadoop的组成HDFS架构设计Yarn架构设计MapReduce架构设计 总结 在大数据时代&#xff0c;Hadoop作为一种开源的分布式计算框架&#xff0c;已经成为处理大规模数据的首选工具。它采用了分布式存储和计算的方式&#xff0c;能够高效地处理海量数据。Had…

基于51单片机的智能监护与健康检测[proteus仿真]

基于51单片机的自行车测速系统设计[proteus仿真] 个人健康检测系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的智能监护与健康检测 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 2&#xff…

Liunx--nginx负载均衡--前后端分离项目部署

一.nginx简介 Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;它以其轻量级、占用资源少、并发能力强而广受欢迎。 详细介绍 开发背景与特点&#xff1a;Nginx由俄罗斯人Igor Sysoev开发&#xff0c;它是一个自由的、开源的软件。Nginx设计上注重性能和效率&#xff0c;能…

图像读取裁剪与人脸识别

图像读取 Image read ⇒ \Rightarrow ⇒ torchvision.datasets from torchvision import datasets dataset datasets.ImageFolder(data_dir, transformtransforms.Resize((512, 512)))Return value illustration dataset[0][0]是PIL.Image objects&#xff0c;这利用IPyth…

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

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

Easy-Jmeter: 性能测试平台

目录 写在开始1 系统架构2 表结构设计3 测试平台生命周期4 分布式压测5 压力机管理6 用例管理6.1 新增、编辑用例6.2 调试用例6.3 启动测试6.4 动态控量6.5 测试详情6.6 环节日志6.7 实时数据6.8 测试结果 7 测试记录7 用例分析8 系统部署8.1普通部署8.2容器化部署 写在最后 写…

嵌入式C语言(三)

typeof() 使用typeof可以获取一个变量或表达式的类型。 typeof的参数有两种形式&#xff1a;表达式或类型。 int i;typeof(i) j 20; --> int j 20;typeof(int *) a; -->int *a; int f(); -->typeof(f()) k;--? int k我们可以看出通过typeof获取一个变量的…

前后端分离Vue+node.js在线学习考试系统gqw7o

与其它应用程序相比&#xff0c;在线学习平台的设计主要面向于学校&#xff0c;旨在为管理员和学生、教师、院系提供一个在线学习平台。学生、教师、院系可以通过系统及时查看公告信息等。 在线学习平台是在Windows操作系统下的应用平台。为防止出现兼容性及稳定性问题&#xf…

使用 Verilog 做一个可编程数字延迟定时器 LS7211-7212

今天的项目是在 Verilog HDL 中实现可编程数字延迟定时器。完整呈现了延迟定时器的 Verilog 代码。 所实现的数字延迟定时器是 CMOS IC LS7212&#xff0c;用于生成可编程延迟。延迟定时器的规格可以在这里轻松找到。基本上&#xff0c;延迟定时器有 4 种操作模式&#xff1a;…

用c# 自己封装的Modbus工具类库源码

前言 Modbus通讯协议在工控行业的应用是很多的&#xff0c;并且也是上位机开发的基本技能之一。相关的类库也很多也很好用。以前只负责用&#xff0c;对其并没有深入学习和了解。前段时间有点空就在这块挖了挖。想做到知其然还要知其所以然。所以就有了自己封装的Modbus工具类库…