一、Redis 相关
1. redisTemplate.keys 方法
- 功能 :RedisTemplate 提供的批量查询 Key 的方法,支持通配符匹配(
*匹配任意字符、?匹配单个字符、[]匹配指定范围)。 - 语法 :
Set<String> keys = redisTemplate.keys("pattern");(如redisTemplate.keys("user:*")匹配所有以user:开头的 Key)。 - 使用注意 :
- 底层执行 Redis 的
KEYS命令,在 Redis 存储海量 Key 时,会阻塞主线程(Redis 单线程),生产环境禁止高频使用; - 替代方案:使用 Redis 有序集合(ZSet)或哈希(Hash)归类存储,避免全量扫描 Key。
- 底层执行 Redis 的
2. Redis 树形数据实现
Redis 本身无原生树形结构,需通过 Key 设计/数据结构模拟:
- 方案1:Key 层级命名 :用
:分隔层级,如category:1:sub:2(表示分类1下的子分类2),结合keys("category:1:*")查询子节点; - 方案2:Hash 存储节点关系 :
hset("tree:category", "1", "子节点ID列表"),hget("tree:category", "1")获取子节点; - 方案3:ZSet 排序层级 :为每个节点设置分数(层级深度),
zadd("tree:category", 1, "节点1"),zrange("tree:category", 0, -1)按层级排序。
二、Spring 核心特性
1. 静态方法无法访问 Spring 注入的非静态成员
- 核心原因 :
- 静态方法属于「类级别」,类加载时即初始化,优先级高于 Spring 容器;
- Spring 注入的成员(如
@Autowired标注的对象)属于「实例级别」,需等 Spring 初始化 Bean 后才存在; - 静态方法执行时,Bean 尚未注入,直接访问会抛出
NullPointerException。
- 解决方案 :
- 避免在静态方法中依赖 Spring 注入的 Bean;
- 通过 Spring 上下文工具类获取 Bean(
ApplicationContext.getBean(Class)); - 将静态方法改为实例方法,配合
@Component让类被 Spring 管理。
2. Spring Cache 缓存框架
(1)基础信息
- 归属 :Spring 框架原生功能(隶属于
org.springframework.cache包),无需额外第三方框架核心依赖; - 核心作用:通过注解简化缓存逻辑,无需手动调用 RedisTemplate 等缓存客户端。
(2)支持的缓存介质
Spring Cache 是「缓存抽象层」,可对接多种底层缓存实现:
| 缓存类型 | 适用场景 |
|---|---|
| Redis | 分布式系统、生产环境(最常用) |
| Caffeine | 单机应用、本地缓存(性能最优) |
| Ehcache | 传统企业级应用 |
| ConcurrentHashMap | 测试环境、简单单机场景 |
- 数据库兼容性:可配合任意数据库(MySQL/Oracle/PostgreSQL 等),仅负责缓存业务查询结果,与底层存储数据库无关。
(3)核心注解及 SpEL 表达式
| 注解 | 核心作用 | 关键参数 & SpEL 示例 |
|---|---|---|
@EnableCaching |
开启 Spring Cache 功能(加在启动类/配置类) | 无 |
@Cacheable |
查询缓存:有缓存直接返回,无则执行方法并缓存结果 | cacheNames="userCache"(缓存块名称) key="#user.id"(取入参user对象的id) key="#p0.id"/#a0.id(取第0个入参的id) key="#result.id"(取方法返回值的id) |
@CachePut |
更新缓存:强制执行方法,将结果更新到缓存(用于新增/修改) | key="#id"(指定缓存Key) |
@CacheEvict |
删除缓存:移除指定Key或清空缓存块 | key="#id"(删除指定Key) allEntries=true(清空当前cacheNames下所有缓存) |
- SpEL 与 @RequestBody 配合 :前端传递 JSON 数据时,
@RequestBody UserDTO userDTO可直接用key="#userDTO.id"取参。
(4)@Cacheable 与 @CachePut 核心区别
| 维度 | @Cacheable | @CachePut |
|---|---|---|
| 方法执行逻辑 | 缓存命中则不执行方法,直接返回缓存值;未命中才执行方法 | 必须执行方法,执行后将结果覆盖到缓存 |
| 适用场景 | 查询操作(如根据ID查用户、查菜品列表) | 新增/全量更新操作(如新增用户、修改菜品信息) |
| 缓存写入时机 | 方法执行后(未命中时) | 方法执行后(强制写入) |
(5)代理方法原理
Spring Cache/事务/AOP 底层均基于「动态代理」实现:
- 核心逻辑 :当调用标注了
@Cacheable/@Transactional的方法时,实际执行的是 Spring 生成的「代理类方法」,而非原方法; - 执行流程:代理类先拦截请求 → 执行缓存判断/事务开启等逻辑 → 再调用原方法 → 最后执行缓存写入/事务提交等逻辑;
- 注意点:同类内调用标注注解的方法,代理不会生效(未走代理类),需通过注入的 Bean 调用。
3. 缓存数据读取流程(用户视角)
用户手机/前端无 Redis 数据库,缓存仅存在于服务器端,完整流程:
- 用户通过前端(APP/网页)发起 HTTP 请求 → 后端服务器;
- 后端接口先查询 Redis 缓存:
- 缓存命中:直接将缓存数据转为 JSON 格式;
- 缓存未命中:查询 MySQL 数据库,将结果写入 Redis 后再转 JSON;
- 后端将 JSON 数据通过 HTTP 响应返回给前端;
- 前端解析 JSON 并展示给用户。
4. @RestController 注解
-
核心作用 :标记类为 SpringMVC 控制器,且所有方法返回值自动转为 JSON 响应(无需手动加
@ResponseBody); -
底层构成 :
@RestController = @Controller + @ResponseBody; -
参数说明:该注解无入参,仅作为「标识注解」,告知 Spring 此类用于处理 HTTP 请求并返回 JSON 数据;
-
示例 :
java@RestController @RequestMapping("/user") public class UserController { @GetMapping("/{id}") public Result<UserDTO> getUserById(@PathVariable Long id) { // 返回值 Result<UserDTO> 自动转为 JSON 响应 return Result.success(userService.getById(id)); } }
5. 接口设计:@Service 与接口的关系
(1)@Service 标注位置
@Service必须标注在实现类 上,而非接口:- 接口无法实例化,Spring 只能创建实现类的 Bean 并纳入容器管理;
- 接口仅定义方法规范,无具体实现逻辑,无法被 Spring 管理。
(2)控制层注入接口类型的原因(面向接口编程)
-
核心意义 :
- 解耦 :控制层仅依赖接口规范,不依赖具体实现,更换实现类(如
UserServiceImplV2)时,控制层无需修改代码; - 易测试:单元测试时可通过 Mock 框架模拟接口实现,无需依赖真实业务逻辑;
- 符合开闭原则:扩展新功能只需新增实现类,无需修改原有代码;
- AOP 兼容:Spring 动态代理默认基于接口生成代理类,更稳定。
- 解耦 :控制层仅依赖接口规范,不依赖具体实现,更换实现类(如
-
示例 :
java// 接口:定义规范 public interface UserService { UserDTO getById(Long id); } // 实现类:加@Service,被Spring管理 @Service public class UserServiceImpl implements UserService { @Override public UserDTO getById(Long id) { // 具体实现 } } // 控制层:注入接口类型 @RestController public class UserController { @Autowired private UserService userService; // 实际注入的是UserServiceImpl的Bean }
6. Mapper 接口的设计逻辑
MyBatis 中 Mapper 仅定义接口,无需编写实现类:
- 核心原理:MyBatis 启动时,通过「动态代理」为 Mapper 接口生成代理实现类;
- 代理类作用:解析 Mapper 接口方法与 XML/注解中的 SQL,自动生成 JDBC 执行逻辑(如参数绑定、结果集映射);
- 优势:开发者只需定义方法名和 SQL,无需编写重复的 JDBC 代码,简化数据库操作。
三、AOP 面向切面编程
1. 切面(AOP)核心定义
- 本质:将与业务无关但需全局统一执行的逻辑(如日志、权限、事务),从业务代码中抽离出来,通过「切面」统一管理,实现「横切逻辑」与业务逻辑解耦;
- 通俗理解:在不修改原有业务代码的前提下,为方法添加「增强功能」(如执行方法前校验权限、执行后记录日志)。
2. 常用 AOP 场景
| 场景 | 作用 |
|---|---|
| 全局日志记录 | 记录接口请求参数、响应结果、执行耗时 |
| 权限校验 | 方法执行前校验用户是否有操作权限 |
| 公共字段自动填充 | 如 @AutoFill 注解,为实体类自动填充 createTime、updateUser 等字段 |
| 事务管理 | 方法执行前开启事务,执行后提交/回滚 |
| 缓存处理 | 配合 Spring Cache 实现缓存逻辑 |
| 异常统一处理 | 捕获方法抛出的异常,返回标准化响应 |
| 接口限流 | 限制接口的请求频率,防止过载 |
四、数据库设计
1. 冗余字段
- 定义 :在数据表中存储可通过关联查询获取的字段(如订单表中存储
username,而该字段原本属于用户表); - 核心作用 :
- 提升查询性能:避免多表联查,直接从当前表获取数据(如查询订单列表时,无需关联用户表即可显示用户名);
- 简化业务逻辑:统计报表、导出数据时,无需复杂联表,降低代码复杂度;
- 数据溯源:即使关联表数据修改(如用户名修改),冗余字段仍能保留历史状态(如订单创建时的用户名)。
- 缺点:增加数据存储成本,需保证冗余字段与源字段的一致性(如通过触发器/代码同步)。
五、项目构建与接口调试
1. Gradle 构建工具
- 核心作用:与 Maven 功能一致,用于 Java 项目的构建、依赖管理、打包部署;
- 优势 :
- 语法简洁(基于 Groovy/Kotlin),配置文件比 Maven 更轻量化;
- 构建速度更快,支持增量构建;
- 兼容性强,可与 Maven 仓库互通;
- 核心功能 :
- 管理项目依赖(自动下载 jar 包);
- 编译源代码、运行单元测试;
- 打包项目(生成 jar/war 包);
- 部署项目到服务器。
2. Swagger 接口工具
- 核心定位:API 文档生成 + 在线接口调试工具;
- 核心价值 :
- 自动生成文档 :通过
@Api/@ApiOperation等注解,自动生成规范化的接口文档,无需手动编写; - 在线调试:后端开发者/前端开发者可直接在网页上填写参数、发送请求、查看响应,无需依赖 Postman 等第三方工具;
- 接口规范统一:前后端基于同一套文档对接,减少沟通歧义。
- 自动生成文档 :通过
3. 前端发送 JSON 数据的方式
JSON 数据不藏在 URL 中,核心传输方式:
-
请求体(Request Body) :
-
适用请求方式:POST/PUT/PATCH(GET 无请求体);
-
请求头必须设置:
Content-Type: application/json; -
示例(前端 Axios 代码):
javascriptaxios.post('/user/save', { name: '张三', age: 20 }, { headers: { 'Content-Type': 'application/json' } });
-
-
URL 传参:仅用于传递简单参数(如 ID、页码),JSON 数据因体积/格式问题,不适合 URL 传输(URL 有长度限制且需编码)。