一、思维导图整体知识结构梳理
1. 数据库三大范式(1NF、2NF、3NF)
- 第一范式 1NF:原子性
- 要求:列不可再拆分,每个字段的值都是最小单元
- 禁止:一个单元格存放多个数据
- 反例:联系方式字段:电话;邮箱
- 改进:拆分为电话、邮箱两个独立字段
- 前提:必须先满足 1NF
- 第二范式 2NF:消除部分依赖
- 要求:非主键字段,必须完全依赖整个主键,不能只依赖主键其中一部分
- 针对:复合主键 (多个字段联合做主键)
- 问题:出现部分依赖→数据冗余、更新异常、删除异常
- 改进:拆表,让字段完全依赖主键
- 前提:必须先满足 1NF + 2NF
- 第三范式 3NF:消除传递依赖
- 要求:非主键字段不能依赖其他非主键字段
- 通俗:非主键列只能依赖主键,列与列之间不能有依赖关系
- 反例:员工表 (员工 id, 姓名,部门 id, 部门名称)
- 改进:拆分两张表:员工表、部门表
- 核心口诀
- 1NF:列不可再分
- 2NF:消除部分依赖
- 3NF:消除传递依赖
- 范式优缺点
- 优点:减少数据冗余、减少增删改异常
- 缺点:拆分多张表,查询需要多 join,速度变慢;开发难度变大,表间关系多,联表查询速度慢
2.Java 注解 Annotation
- 注解本质:继承了 Annotation 接口的特殊接口;编译后生成:代理类,运行时通过反射生成代理对象
- 注解不是程序本身,可以理解成代码上的标记、标签;本身不会直接执行任何业务逻辑
- 注解能干什么
- 给类 / 方法 / 变量打上标记
- 通过反射读取这个标记,然后程序根据标记做对应的功能
- 三种注解生命周期(元注解 @Retention)
- SOURCE 源码阶段:只存在于.java 源文件,编译后就消失,如 @Override
- CLASS 字节码阶段:存入 class 文件,入 JVM 加载类时丢弃 (默认生命周期)
- RUNTIME 运行时:一直存活,程序运行期间可以通过反射获取 (自定义注解必选!)
- 四大元注解 (修饰注解的注解)
- @Target:注解能加在哪些位置 (类、方法、属性)
- @Retention:生命周期
- @Documented:生成 javadoc 文档可见
- @Inherited:子类可以继承父类上的注解
- 内置基础注解:@Override 重写、@Deprecated 过时、@SuppressWarnings 压制警告
- 自定义注解解法:
public @interface MyAnnotation{}
3.ClassLoader 类加载器(拓展)
-
作用:负责把.class 字节码文件,加载到 JVM 内存,生成对应的 Class 对象
-
JVM 三种类加载器(双亲委派模型)
- 启动类加载器 (Bootstrap ClassLoader):C++ 编写,加载 JDK 核心类 (java.lang.String、Object)
- 扩展类加载器 (Extension ClassLoader):加载 jdk 扩展目录下的 jar 包
- 应用程序类加载器 (App ClassLoader):我们自己写的项目代码、第三方 jar 包
-
双亲委派模型执行流程
- 不会自己先加载→向上交给父类加载器
- 父加载器无法加载 (找不到该类),才向下,子类加载器自己加载
- 好处:保证核心类安全,防止篡改 JDK 核心类 (例如自己写一个 String 类);类全局唯一,不会重复加载
-
获取类加载器代码示例
4.AOP 权限校验‑AuthAop 切面
- 注解开发:@Component 交给 spring 容器管理;@Aspect:开启切面;自定义注解 @HasPermission
- 校验流程
- ThreadLocal 获取登录用户 id
- 根据用户 id 查询用户权限列表
- 判断用户是否拥有接口所需权限
- 有权限‑放行;无权限‑抛出异常,AOP 禁止访问
- 拦截器链执行顺序:过滤器最先执行→AOP 切面权限校验→接口
5. 线程的 6 种状态
- NEW 新建:new 出来线程对象,未调用 start ()
- RUNNABLE 可运行就绪 + 运行:调用 start () 之后,等待 CPU、正在运行
- BLOCKED 锁阻塞:等待 synchronized 同步锁,获取不到锁就进入阻塞
- WAITING 无限等待:被释放,等待其他线程唤醒 notify
- TIMED_WAITING 计时等待:等待指定时长,时间到自动苏醒
- TERMINATED 终止:run () 执行完毕,线程死亡
- 核心区别:sleep:计时等待,不释放锁;wait:无限等待,释放锁
6. 线程的创建方式(回顾)
- 继承 Thread 类:重写 run ();对象.start () 启动线程;缺点:受 java 单继承限制
- 实现 Runnable 接口【推荐】:任务对象传入 Thread;start () 启动;优点:解耦,无继承限制,Runnable 匿名内部类
- 匿名内部类 / Lambda 简写;lambda 一行简写:()->{}
- Callable + FutureTask(带返回结果):有返回值,可以抛出异常;get () 获取线程结果
7.JWT 令牌解析
- 解析位置:登录校验拦截器
- 解析步骤
- 获取请求头中的 token
- 判断 token 是否存在
- 使用相同密钥解析令牌
- 获取载荷里面的用户信息
- 将用户 id 存入 ThreadLocal
- 放行
- 解析异常情况
- token 为空→401 未登录
- 令牌过期→401
- 密钥被篡改→401
- 密钥不一致→401
- 注意事项:生成、解析 JWT 密钥必须完全一致;载荷中不能存放密码等敏感信息
8.AOP 进阶‑权限相关
AOP 基础回顾
- 核心术语:切入点 Pointcut、通知 Advice、切面 Aspect、目标对象 Target
- 五种通知类型:@Before 前置、@AfterReturning 返回后、@AfterThrowing 异常、@After 最终通知、@Around 环绕通知 (可控制执行)
三种权限拦截方案对比
- Filter 过滤器:执行位置‑Servlet 层,Spring 容器外部;局限:无法注入 Spring 的 Bean
- Interceptor 拦截器:执行位置‑Controller 层前后;局限:仅能拦截控制器,切不到 Service 业务层
- AOP 切面:执行位置‑Service、Controller 层均可拦截;优势:细粒度方法级权限、业务代码解耦
AOP 实现权限控制思路
- 自定义权限注解 @HasPermission ("user:add")
- 目标方法添加权限注解
- AOP 切面拦截带有注解的方法
- 获取当前登录用户的权限集合
- 校验用户权限是否满足要求
- 校验通过‑放行;校验失败‑抛出无权限异常
自定义权限注解开发
- @Target:标注注解可以加在方法上
- @Retention (RetentionPolicy.RUNTIME):运行时生效
- 属性:String value () default ""; 存放权限标识
- @Aspect + @Component 声明切面类
- 切入点表达式:@annotation (自定义注解全类名)
- 使用 @Around 环绕通知 (最适合做权限控制)
- 获取注解上的权限标识
权限功能类开发
- ThreadLocal 存储登录用户;作用:同一个线程内共享用户信息
- 工具类 UserContext:setUser ()、getUserId ()、remove () 必须清理,防止内存泄漏
- 权限校验逻辑:用户为空→未登录;用户权限列表 contains (所需权限)→放行;无权限→抛异常
AOP 权限优缺点
- 优点:权限统一管理,便于维护;可拦截 Service 层,实现方法级细粒度权限控制
- 缺点:不能拦截静态方法、final 方法;内部调用 (this. 方法) AOP 失效
AOP 失效常见缺点
- this 调用本类方法,不会走切面;方法没有交给 spring 容器管理 (缺少 @Component);切入点表达式写错,匹配不到目标方法
AOP 权限 vs RBAC
- AOP:代码层面校验用户当前有没有该权限标识
- RBAC:数据库层面 (用户‑角色‑权限);二者配合:RBAC 查权限数据,AOP 做权限拦截
二、思维导图制作意图
本次思维导图围绕 AOP 进阶权限开发展开,打通从数据库三大范式、Java 注解原理、类加载器双亲委派模型、线程基础、JWT 令牌解析,到 AOP 实现细粒度权限校验完整链路。对比过滤器、拦截器、AOP 三种拦截方案的区别,掌握自定义注解 + 环绕通知做权限控制的实战思路,同时补齐 ThreadLocal 线程传参,AOP 失效常见坑点等高频面试考点,为后端 RBAC 权限项目开发打下基础。
三、当日学习总结
今天学习了数据库三大范式优化数据表结构;回顾 Java 注解底层原理与自定义注解开发;学习 JVM 类加载双亲委派机制;复习线程 6 种状态、4 种线程创建方式;掌握 JWT 令牌在拦截器中解析流程;重点攻克 AOP 进阶实战:自定义注解 + AOP 环绕通知完成接口权限校验;区分 Filter、Interceptor、AOP 三者拦截层级;理解 ThreadLocal 线程共享数据以及 AOP 内部调用失效的问题,学会 AOP 与 RBAC 数据库权限模型搭配使用,完成方法级别的细粒度权限控制。
