SpringBoot
type
Post
status
Published
date
Apr 14, 2025
slug
springboot
summary
tags
category
Java Web
icon
password
Parent item
Sub-item
Place
Spring
IOC
IOC:控制反转,在传统开发过程中,如果我们要使用一个对象,我们需要手动new一个对象,这样耦合性太高,IOC的核心是将对象的创建、组装以及生命周期的管理从程序代码转移到外部容器(Spring),而Spring帮我们实现了这一过程,我们只需要声明和使用即可。
DI
依赖注入
当一个类需要依赖另一个类的对象时,就必须在类中直接new这个类的对象,这样耦合性太高了,依赖注入指的是将对象的创建和依赖关系的管理交给Spring容器来实现,类只需要声明要依赖的对象即可,Spring容器会在允许是将依赖对象注入到类中,降低耦合度
AOP
指的是面向切面编程,他允许我们在不修改原来代码的情况下,对其进行逻辑增强。
他的实现是依赖于动态代理,即JDK动态代理和CGlib动态代理,如果target实现了接口,就用JDK动态代理,如果没有,就用CGlib动态代理
这两个的区别就是JDK动态代理中代理类是实现了相同的接口,并且代理类中含有目标类对象,在调用目标类方法前后进行逻辑增强。CGlib动态代理是继承了目标类,通过重写方法进行逻辑增强
主要概念:(这考那就是sb了,上面的足够了)
- Aspect:切面
- Pointcut:切点
- Advice:通知
- Joint point:连接点
- target:目标对象
- AOP proxy:代理对象
考虑过AOP的性能问题嘛?
在实际生产AOP 的性能开销通常是纳秒级或微秒级的。
相比于它代理的业务逻辑(比如数据库 IO、网络请求、复杂计算),AOP 本身的损耗几乎可以忽略不计。除非是在极度追求性能的高频计算场景(比如每秒百万次调用的纯内存计算),否则我们不认为 AOP 是性能瓶颈。
事务
编程式事务
声明式事务(一般使用的,基于注解和AOP来声明)
实际调用的是代理类中的invoke方法
- 事务只用作用到public方法上才生效
- 底层的数据库必须支持事务(myisam就不行)
- propagation(传播):事务传播行为
- REQUIRED(required,必须的,默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
- REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说无论如何都会创建新的事务,且相互独立,互不影响。
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
- NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
- MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
- NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
- NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行
- isolation:事务隔离级别
- DEFAULT(默认):使用底层数据库的默认隔离级别
- READ_UNCOMMITTED:读未提交
- READ_COMMITTED:读已提交
- REPEATABLE_READ:可重复读
- SERIALIZABLE:串行化
- timeout:事务超时时间(秒)
- readOnly:是否只读事务(默认为false)
- rollbackFor/rollbackForClassName:指定哪些异常触发回滚
- noRollbackFor/noRollbackForClassName:指定哪些异常不触发回滚
说明:
- 这个事务配置表示:
- 使用REQUIRED传播行为(默认值,可省略)
- 使用READ_COMMITTED隔离级别
- 超时时间为30秒
- 非只读事务(默认值,可省略)
- 遇到BusinessException或SQLException会回滚
- 遇到IllegalArgumentException不会回滚
- 当
placeOrder方法被调用时,Spring会开启一个事务,如果方法正常执行完成,事务会被提交;如果抛出BusinessException或SQLException,事务会被回滚;如果抛出IllegalArgumentException,事务不会回滚。
- 如果
paymentService.processPayment()也使用了事务注解,根据传播行为的不同,可能会加入当前事务或开启新事务。
循环依赖
- 构造方法产生的循环依赖问题
- setter方法,且对象是原型
- setter方法,对象是单例模式
Spring只解决了第3种情况下的
1级缓存存储的是完全好的
2级缓存存储的是实例化,但还未属性填充的
3级缓存存储的是工厂对象
image-20250412111530343

Bean的生命周期
- 实例化(Instantiation)
- 属性赋值(Populate)
- 初始化(Initialization)
- 使用
- 销毁(Destruction)
实例化:指的是为Bean分配内存空间,实例化一个Bean对象。这里如果Bean是个单例模式的化,会判断容器中是否已经存在了这个对象,如果有的话直接取出来使用
属性赋值:为Bean的属性进行赋值和依赖注入(循环依赖问题就是发生在这个阶段)
初始化:执行各种通知和初始化方法,这里通过
BeanPostProcessor和Aware接口提供了大量扩展点使用Bean
销毁Bean:执行销毁Bean对象的操作
SpringBoot
简单介绍一下SpringBoot
- 默认集成了多种内嵌服务器,比如说tomcat,Jetty
- 简化开发:SpringBoot采用了自动配置的机制,根据程序中引入的依赖和配置,SpringBoot可以自动配置整个应用程序的环境,大大减少了开发者需要进行的手动配置操作
- 快速启动:SpringBoot快速的项目启动器,通过引入不同的starter,可以快速集成需要的框架爱和库
- 遵循约定大于配置的原则,提高了一系列默认的配置和约定,如果开发者按照这些约定开发,可以大大减少配置文件的编写,可以更加专注于业务代码的实现。
SpringBoot自动装配原理
- SpringBoot项目中的启动类上有一个@SpringBootAplicatioon,他对3个注解进行了封装
- SpringBootConfiguratiion
- EnableAutoConfiguration
- ComponentScan
- 其中EnableAutoConfiguration使实现自动装配的核心
- 他里面有一个import注解,通过他我们先从spring.factories文件中获取获取候选配置类。
- 排除不满足当前环境的配置类。
- 利用有效的配置类 按需实例化这些 Bean
常用注解
按照程序启动来思考
- SpringBootAplicatioon,用于标识应用程序的入口点
- Controller:标明控制器类
- Autiwired:自动注入
- Configuration:标明是一个配置类
- RequestMapping:映射HTTP请求到Controller的方法上
- 类似的有GetMapping,DeleteMapping等等
MyBatis-plus
背这个表就行
维度 | MyBatis | MyBatis-Plus |
SQL 编写 | 必须手动编写(XML 或注解),哪怕是简单的 selectById。 | 通过BaseMapper,单表 CRUD 无需编写 SQL。 |
动态 SQL | 使用 XML 标签如 <if>, <where>, <foreach>。 | 提供 Wrapper 条件构造器,复杂的条件查询 |
分页插件 | 通常需引入第三方 PageHelper。 | 自带分页插件,通过配置 Interceptor 即可实现。 |
功能扩展 | 需自行扩展(如逻辑删除、自动填充)。 | 内置逻辑删除 ( @TableLogic)、自动填充、序列化插件等。 |
第一个就是mybatis通过BaseMapper使得单表的CRUD无需编写SQL,直接调用函数即可。第二就是提供了wrapper条件构造器,支持复杂的条件查询。第3个就是新增了注解,可以将实体类和数据库表对应,以及字段与列对应。除此之外还支持分页等等
BaseMapper
正如你所说,MyBatis 需要你为每一个简单的增删改查(CRUD)编写 SQL(通常在 XML 中)。而 MyBatis-Plus 通过
BaseMapper<T> 接口,将这些操作封装成了 Java 函数。- MyBatis: 你需要定义接口,并在 XML 中写
SELECT * FROM user WHERE id = #{id}。
- MyBatis-Plus: 你的 Mapper 只需要继承
BaseMapper<User>,就可以直接调用userMapper.selectById(1)。
原理: MP 在启动时会根据你的实体类(Entity)信息,自动生成注入这些基础的 SQL 语句。
具体示例
方法名 | 说明 | 对应SQL |
insert(T entity) | 插入 | INSERT INTO… |
deleteById(Serializable id) | 按ID删除 | DELETE FROM…WHERE id=… |
updateById(T entity) | 按ID更新 | UPDATE…WHERE id=… |
selectById(Serializable id) | 按ID查询 | SELECT * FROM…WHERE id=… |
selectBatchIds(Collection ids) | 批量ID查询 | SELECT * FROM…WHERE id IN… |
selectList(Wrapper wrapper) | 条件查询 | SELECT * FROM…WHERE… |
selectPage(Page page, Wrapper wrapper) | 分页查询 | SELECT * FROM…LIMIT… |
新增注解
你提到的“实体类加注解”是 MP 实现自动化 SQL 的基础。
@TableName: 指定实体类对应的数据库表名。
@TableId: 指定主键,并可以配置主键生成策略(如雪花算法、自增等)。
@TableField: 处理字段名与数据库列名不一致的情况,或者排除非数据库字段。
这种方式极大地减少了 ResultMap 的配置工作,让代码更加整洁。
Wrapper
这是 MP 的灵魂。它允许你通过 Java 代码动态构建复杂的
WHERE 条件,而不需要在 XML 里写大量的 <if> 标签。内置的分页插件
- MyBatis: 通常需要引入
PageHelper插件。
- MyBatis-Plus: 自带分页插件,你只需要配置一个 Interceptor,然后在代码里传一个
Page对象即可。
自动填充与逻辑删除
- 自动填充: 比如
create_time和update_time,可以通过配置在插入或更新时自动赋值,不需要手动set。
- 逻辑删除: 只需一个
@TableLogic注解,delete操作就会自动变成update set deleted = 1,查询时也会自动过滤掉已删除数据。
ThreadLocal
1.什么是ThreadLocal
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
从源码上看,
Thread 类中有一个成员变量 ThreadLocalMap。ThreadLocal 仅仅是作为一个入口和Key。 当执行 threadLocal.set(value) 时,它会分三步走:- 首先获取
Thread.currentThread()。
- 然后拿到该线程对象的
ThreadLocalMap。
- 最后以 ThreadLocal 对象自身为 Key,将 value 存入这个 Map。
取值的时候也是同理,通过当前 ThreadLocal 对象去当前线程的 Map 里查。既然 Map 是属于线程私有的,自然就实现了物理上的隔离。”
other(了解)
.gitgnore:
git需要忽略的配置文件
AOP示例代码
切面类
target类,不需要任何修改
概念
- Aspect:指的是LogAspect这个类
- Pointcut:指的是定义什么时候代理
- Advice:做了什么增强的逻辑
- Joint point:被代理的地方都叫
- target:目标对象
上一篇
How to create a MCP Server
下一篇
The clever use of search engines
Loading...