1.理论收获
1.1 Day1 开发环境搭建
常见的名称说明,细化POJO说明

Git版本控制:创建本地仓库,gitee创建远地仓库,推送到远程仓库
前后端连通示意图

前端发送到后端的请求地址不同?

做了一个nginx反向代理,也就是将前端的动态请求由nginx转发到后端服务器
**nginx 作用:**负载均衡(基于反向代理实现),保证后端安全(不向外面暴露地址),反向代理(url映射),提高访问速度(缓存加速)

**Swagger作用:**生成接口文档,在线接口测试
Knife4j是为JavaMVC框架集成Swagger生成Api文档的增强解决方案。(简化Swagger)
图中是Swagger帮助生成接口文档的常见注解,生成后的文档添加注解

1.2 Day2 员工的相关管理操作(增删改查分页)
- 新增员工
- 员工分页查询
- 启用禁用员工账号
- 编辑员エ
- 导入分类模块功能代码
根据接口设计对应的DTO格式,通过DTO的拷贝对实体中通用字段进行赋值,然后再对实体类中独有的字段进行赋值

把时间进行转化为年月日格式
第一种方法:通过注解

第二种方法:扩展Spring MVC的消息转换器
通过自定义Jackson2ObjectMapperBuilderCustomizer或MappingJackson2HttpMessageConverter,统一配置时间类型的序列化 / 反序列化规则,全局生效。适合「所有时间字段都要统一格式(如全为 yyyy-MM-dd)」的场景。
路径参数需要加@PathVariable注解

上传的是JSON格式的数据,需要加一个@RequestBody注解进行接收

可以使用DTO格式的数据直接对实体进行赋值,然后再对实体类里面独有的字段进行赋值。
这里的BaseContext是一次登录就是一个单独的线程,在登录后进行用户鉴权的时候就把ID进行了保存。

1.3 Day3 菜品管理
- 公共字段自动填充
- 新增菜品
- 菜品分页查询
- 删除菜品
- 修改菜品
对于一些公共字段进行自动填充,解决代码冗余,后续代码更改需要大范围修改代码的问题
如下表所示的公共字段在用户表和菜单表都存在

针对该问题的解决思路为:(涉及技术:枚举,注解,AOP,反射)
- 自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法
- 自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值
- 在Mapper 的方法上加入AutoFill注解
开发文件上传接口: 浏览器 → 后端服务 → 阿里云OSS
一些注解的解释

UUID 的全称是 Universally Unique Identifier(通用唯一识别码),简单说就是:
一种通过特定算法生成的、全球唯一的 32 位十六进制数字字符串(通常会用
-分成 5 段,格式如8-4-4-4-12),几乎不可能重复。
@Transactional 这个注解保证操作的原子性
@Autowired 让 Spring 自动帮你创建并注入需要的对象,不用自己手动 new 实例。
@RequestParam 获取 HTTP 请求中的参数值,并将其绑定到控制器方法的参数上
SQL的动态语句拼接的写法
实际的拼接效果如下:

同理的SQL语句动态拼接,相较于单条语句删除可以优化性能

1.4 Day5 Redis相关
- Redis入门
- Redis数据类型
- Redis常用命令
- 在Java中操作Redis
- 店铺营业状态设置
Redis是一个基于内存的key-value结构数据库
- 基于内存存储,读写性能高
- 适合存储热点数据(热点商品、资讯、新闻)
- 企业应用广泛
5种常用的数据类型

各种数据类型的特点

- 字符串(string):普通字符串,Redis中最简单的数据类型
- 哈希(hash):也叫散列,类似于Java中的HashMap结构
- 列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
- 集合(set):无序集合,没有重复元素,类似于Java中的HashSet
- 有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
字符串的命令

哈希操作的命令

列表的操作命令

集合操作命令

有序集合操作命令

通用命令符

@Configuration 注解的类可以看作是 Spring 容器的「配置说明书」
- 它告诉 Spring:这个类里定义了创建 Bean(Spring 管理的对象)的规则和方法。
- 替代了传统 Spring 配置中的 XML 文件(比如
applicationContext.xml),实现「注解驱动的配置」。 - Spring 启动时会扫描并解析这个类,执行里面的方法创建 Bean,并把这些 Bean 放入 IoC 容器中供整个应用使用。
IOC(Inversion of Control,控制反转)容器是 Spring 框架的核心
- 它是一个内存中的对象池 / 仓库,负责管理所有被 Spring 接管的对象(这些对象称为 Bean);
- 核心职责:
- 创建 Bean :根据你写的配置(比如
@Configuration+@Bean、@Component等)创建对象; - 管理依赖:自动把 Bean 之间的依赖关系注入(比如把 UserDao 注入到 UserService 中);
- 管理生命周期:控制 Bean 的创建、初始化、使用、销毁全过程;
- 提供获取 Bean 的方式:你可以通过名称、类型从容器中拿到需要的对象。
- 创建 Bean :根据你写的配置(比如
序列化器的作用
- 数据格式转换:实现 Java 数据类型 ↔ Redis 字节数组的双向转换,是 Java 程序与 Redis 交互的「桥梁」;
- 保证数据完整性:确保对象存储到 Redis 后,取出来时能完整还原(比如字段不丢失、类型不错乱);
- 统一编码规则:避免因编码不一致导致的乱码(比如字符串用 UTF-8 编码,而不是 GBK);
- 适配不同场景:不同序列化器有不同的特性(比如速度、兼容性、可读性),可适配缓存、分布式锁、消息队列等不同场景。
1.5 Day6 微信小程序开发
- HttpClient
- 微信小程序开发
- 微信登录
- 导入商品浏览功能代码
实现跟前端差不多,结构差不多
1.6 Day7 菜品缓存
- 缓存菜品
- 缓存套餐
- 添加购物车
- 查看购物车
- 清空购物车
缓存和数据库内容的一致性:修改数据和删除数据的时候要注意清理缓存数据。
Spring Cache是一个框架,实现了基于注解 的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache提供了一层抽象,底层可以切换不同的缓存实现,例如:EHCache、Caffeine、Redis
Spring Cache的常见注解

1.7 Day8 用户下单
- 导入地址簿功能代码
- 用户下单
- 订单支付
@Transactional 加一个事务注解,确保一致性。
1.8 Day10 Spring Task
- Spring Task
- 订单状态定时处理
- WebSocket
- 来单提醒
- 客户催单
SpringTask是spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
应用场景:
- 信用卡每月还款提醒
- 银行贷款每月还款提醒
- 火车票售票系统处理未支付订单
- 入职纪念日为用户发送通知
cron表达式 其实就是一个字符串,通过cron表达式可以定义任务触发的时间
构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)
不需要记忆,可以通过在线生成器进行生成表达式
Spring Task使用步骤:
- 导入maven坐标spring-context(已存在)
- 启动类添加注解@EnableScheduling开启任务调度
- 自定义定时任务类
简单实例:

关于代码开发过程中对于组件,注解,注入,容器等的认识和疑惑
| 技术概念 | 餐馆场景比喻 |
|---|---|
| Spring 容器 | 餐馆的「后厨」:统一管理所有食材、厨具、厨师(对应程序里的对象) |
| 组件(Component) | 后厨里的「标准化工具 / 人员」:比如炒勺、厨师、配菜员(被 Spring 管理的对象) |
| 注解(Annotation) | 贴在工具 / 人员身上的「标签」:比如「炒勺」「川菜厨师」「配菜员」标签 |
| 注入(Autowired) | 「按需分配」:比如给炒菜的厨师自动递上炒勺(给对象自动赋值它依赖的其他对象) |
核心概念:
1. 注解(@XXX):给程序贴「标签」
注解是 Java 的一种特殊标记,本质是「元数据」(描述数据的数据),核心作用:
- 告诉 Spring:这个类 / 方法 / 变量是什么角色、该怎么处理;
- 替代传统 XML 配置,让代码更简洁。
通俗理解 :就像快递上的「易碎」「生鲜」标签,快递员看到就知道怎么处理;Spring 看到 @Component 就知道「这个类要被我管理」。
2. 组件(@Component 及衍生注解):标记「要被 Spring 管理的对象」
@Component 是最基础的「组件注解」,作用是:告诉 Spring「这个类的对象我要管,你帮我创建并放到容器里」。
| 注解 | 适用场景 | 餐馆比喻 |
|---|---|---|
| @Component | 通用组件(无明确业务层) | 后厨通用工具(抹布) |
| @Service | 业务逻辑层(Service) | 厨师(核心业务处理) |
| @Controller | 控制层(Controller) | 前厅服务员(接收请求) |
| @Repository | 数据访问层(Dao/Mapper) | 采购员(和数据库交互) |
3. 注入(@Autowired):给组件「自动分配依赖」
@Autowired 是「自动注入注解」,核心作用:告诉 Spring「这个变量需要你从容器里找一个对应的对象赋值」。可以不用 手动 new 一个对象。
注入的核心规则(新手必知)
- 按类型注入 :Spring 默认根据变量的「类型」(比如
UserDao)去容器里找对应的 Bean; - 必须有对应的组件 :要注入的
UserDao必须加了@Repository/@Component注解(否则容器里没有这个对象,会报错); - 单例特性:容器里的 Bean 默认是单例,所有注入的都是同一个对象(节省内存)。
Spring启动后的执行逻辑

WebSockt

应用场景:
- 视频弹幕
- 网页聊天
- 体育实况更新
- 股票基金报价实时更新
1.8 Day11 营业额统计
- Apache ECharts
- 营业额统计
- 用户统计
- 订单统计
- 销量排名Top10
关于前端传参方式 + 后端接收方式
| 传参位置 | 适用请求类型 | 前端传参示例 | 后端接收注解 | 适用场景 |
|---|---|---|---|---|
| URL 路径 | GET/PUT/DELETE | /user/1(1 是用户 ID) |
@PathVariable |
传递资源 ID、状态等简单值 |
| URL 参数(Query) | GET | /user?name=张三&age=20 |
@RequestParam |
列表查询、筛选条件 |
| 请求体(Body) | POST/PUT/PATCH | JSON 格式的复杂数据 | @RequestBody |
新增 / 更新对象、提交表单 |
| 请求头(Header) | 任意 | Token、Content-Type 等 | @RequestHeader |
传递令牌、编码格式等元数据 |
- 场景 1:URL 路径传参(@PathVariable)
核心逻辑 :把参数嵌在 URL 路径里(比如 /user/1),后端通过 @PathVariable 提取。
java
// 前端(Vue/React 等)
axios.get('/user/1') // 1 是要传递的用户ID
.then(res => console.log(res))
// 后端 Spring Boot 接口
@RestController
@RequestMapping("/user")
public class UserController {
// 路径中的 {id} 对应前端传的 1
@GetMapping("/{id}")
public Result getUserById(@PathVariable Long id) {
System.out.println("接收的用户ID:" + id); // 输出:1
return Result.success();
}
}
- 场景 2:URL 参数(Query)传参(@RequestParam)
核心逻辑 :参数跟在 URL 后,以 ? 分隔,& 连接多个参数(比如 /user?name=张三&age=20),后端用 @RequestParam 接收。
java
// 方式1:直接拼在 URL 里
axios.get('/user?name=张三&age=20')
// 方式2:用 params 配置(推荐,自动编码)
axios.get('/user', {
params: {
name: '张三',
age: 20
}
})
@GetMapping("/user")
public Result getUserByCondition(
// 接收 name 参数,required = false 表示非必传
@RequestParam(required = false) String name,
// 接收 age 参数,指定默认值(不传时用 0)
@RequestParam(defaultValue = "0") Integer age
) {
System.out.println("姓名:" + name + ",年龄:" + age); // 输出:张三,20
return Result.success();
}
- 场景 3:请求体(Body)传参(@RequestBody)
核心逻辑 :把复杂数据(对象、数组)放在请求体中,以 JSON 格式传递,后端用 @RequestBody 接收(最常用的传参方式)。
java
// 前端传递用户对象(新增用户)
axios.post('/user', {
name: '李四',
age: 25,
address: '北京市'
})
// PUT 请求(更新用户)
axios.put('/user/2', {
name: '李四',
age: 26
})
// 实体类(字段名要和前端 JSON 的 key 一致)
public class User {
private String name;
private Integer age;
private String address;
// 必须有无参构造方法(Spring 反射需要)
public User() {}
// getter/setter 省略
}
// 新增用户(POST + @RequestBody)
@PostMapping("/user")
public Result addUser(@RequestBody User user) {
System.out.println("用户名:" + user.getName() + ",年龄:" + user.getAge());
return Result.success();
}
// 更新用户(PUT + @RequestBody)
@PutMapping("/user/{id}")
public Result updateUser(
@PathVariable Long id, // 路径传 ID
@RequestBody User user // 请求体传更新的用户信息
) {
System.out.println("更新用户ID:" + id + ",新姓名:" + user.getName());
return Result.success();
}
- 场景 4:请求头(Header)传参(@RequestHeader)
核心逻辑 :把元数据(比如 Token、语言类型)放在请求头里,后端用 @RequestHeader 接收。
java
// 前端传递 Token 到请求头
axios.get('/user/info', {
headers: {
'Authorization': 'Bearer 123456789', // Token
'Language': 'zh-CN' // 语言类型
}
})
@GetMapping("/user/info")
public Result getUserInfo(
// 接收 Authorization 头
@RequestHeader("Authorization") String token,
// 接收 Language 头,非必传
@RequestHeader(value = "Language", required = false) String language
) {
System.out.println("Token:" + token); // 输出:Bearer 123456789
System.out.println("语言:" + language); // 输出:zh-CN
return Result.success();
}
注意事项
- @RequestBody 只能用一次 :一个请求只能有一个请求体,所以一个接口只能有一个
@RequestBody注解; - 参数名要一致 :前端传的
name要和后端实体类 / 参数名一致(大小写敏感),不一致可加@JsonProperty映射
- GET 请求不能传 Body:HTTP 规范中 GET 请求没有请求体,前端用 GET 传 Body 后端收不到,需改用 POST/PUT;
- 非必传参数要配置 :用
required = false或defaultValue,否则前端不传会报错。
销量排名Top10设计:使用名称查询,借助 group by 语句以及 sum 求和语句
1.9 Day 12 运营数据报表
- 工作台
- Apache POI
- 导出运营数据Excel报表
2.通过观看课件和文档的进一步收获
2.1 扫盲类
2.1.1 常见的项目网站中提供的各种资料分别是什么,如脚手架,E-R图等
-
项目脚手架 (框架).zip:这是项目的基础框架模板,包含了已经配置好的工程结构、依赖、通用工具类(比如统一响应封装、异常处理、日志配置、Redis / 数据库连接等)。
-
项目文档.zip:这是项目的整体说明文档。
-
ER 图.zip:这是数据库实体关系图(Entity Relationship Diagram),用可视化方式展示数据库表结构和表之间的关联关系(比如用户表和订单表的一对多关系)。
-
数据库设计文档.zip:这是详细的数据库设计说明,比 ER 图更具体
-
⭐⭐⭐项目源码.zip(核心资料):这是完整的项目源代码,包含所有业务代码、配置文件、静态资源等。
2.1.2 网上常讲的中间件,技术栈这些都是些什么啊
**技术栈:**做项目从到到尾涉及到的所有技术,合在一起就叫做技术栈
例如:后端技术栈
- 语言:Java
- 框架:SpringBoot、SpringMVC、MyBatis/MyBatis-Plus
- 数据库:MySQL
- 缓存:Redis
- 工具:Maven、Git、Swagger
- 部署:Linux、Docker
常见的JAVA后端技术栈
- 核心框架(必学)
- Java 8 / 11开发语言
- Spring Boot项目开发骨架,一切的基础
- Spring MVC写接口、接收前端请求
- Spring Cloud / Alibaba微服务一套(大项目用)
- MyBatis / MyBatis-Plus操作数据库
- 工具类
- Maven / Gradle管理依赖、打包项目
- Git代码版本管理
- Swagger / Knife4j自动生成接口文档
- Lombok简化代码(@Data、@Slf4j)
- 数据库
- MySQL最主流数据库
- PostgreSQL国企、金融常用
- 缓存
- Redis缓存、分布式锁、限流
中间件: 项目里,不是你写的业务代码,但又必须用的工具软件,统称中间件。作用是帮助解决通用问题,不用你重复造轮子。
例如:
- Redis ------ 缓存中间件:加速查询、减轻数据库压力
- MySQL ------ 数据库(也算广义中间件):存数据
- MQ(消息队列)RabbitMQ / RocketMQ / Kafka:异步、削峰、解耦
- Nginx ------ 反向代理、负载均衡:分发请求、扛高并发
- Elasticsearch ------ 搜索中间件:快速搜索大量数据( like 百度搜索)
- MinIO / OSS ------ 文件存储:存图片、视频、文件
- Gateway / Spring Cloud Gateway ------ 网关:统一入口、鉴权、限流
企业中最常见的中间件
- 缓存中间件
- Redis(99% 项目都用)作用:加速、存会话、分布式锁
- 消息队列 MQ(异步、削峰)
- RabbitMQ中小型公司最常用
- RocketMQ阿里、电商、高并发常用
- Kafka大数据、日志、高吞吐
- 搜索引擎
- **Elasticsearch(ES)**做搜索、日志分析
- 网关 / 负载均衡
- Nginx部署、反向代理、负载均衡
- Spring Cloud Gateway微服务网关
- 服务治理(微服务)
- Nacos服务注册、配置中心
- Sentinel限流、降级、熔断
- 分布式任务调度
- XXL-Job定时任务、分布式调度
- 分布式事务
- Seata保证多服务数据一致
- 文件存储
- MinIO自己搭建文件服务器
- 阿里云 OSS / 腾讯云 COS云存储
- 监控
- Prometheus + Grafana监控服务器、接口、性能
总结:
- 技术栈 = 做项目用到的所有技术
- 中间件 = 项目里的工具软件(Redis、MySQL、MQ、Nginx 等)
- 你写的代码 = 业务逻辑
- 中间件 = 帮你支撑业务、提高性能、解决难题
- 技术栈是大集合 ,中间件是里面的一小类。
2.1.3 持久层的定义
持久层(Persistence Layer)是后端项目中专门负责「把数据存到数据库 / 从数据库取数据」的一层代码,核心作用是隔离业务逻辑和数据存储细节。
- 持久:指数据「永久保存」(比如存到 MySQL、Redis,而不是内存里 ------ 内存里的数据重启就没了);
- 层:指项目代码的「分工模块」(就像公司的部门:销售部管卖货,技术部管开发,持久层只管数据存取)。
核心作用:
- 降低耦合
- 统一管理数据存取逻辑,方便维护
- 封装通用操作,避免重复代码
持久层和其他层的关系

2.1.4 常见注解
@Data 注解:@Data 是 Lombok 框架 提供的注解,自动为 Java 类生成 getter/setter、toString、equals、hashCode、无参构造方法 等通用方法,不用你手动写这些重复的模板代码。
@PostMapping 是 Spring MVC 提供的注解,专门标记这个接口只接收 HTTP POST 类型的请求,同时可以指定接口的访问路径。
@RequestBody 是 Spring MVC 提供的参数注解,作用是:把前端 POST 请求体中 JSON 格式的数据,自动转换成 Java 对象(实体类),绑定到方法的参数上。
@RestController 是 Spring MVC 提供的组合注解,本质 = @Controller + @ResponseBody。标记这个类是 Spring MVC 的控制器 (Controller);让类中所有方法的返回值自动序列化为 JSON 格式,直接作为 HTTP 响应体返回给前端。
@RequestMapping 是 Spring MVC 提供的注解,作用是为整个控制器类下的所有接口 设置统一的路径前缀
@AllArgsConstructor :生成「全参构造方法」(PageResult(long total, List records));
@NoArgsConstructor :生成「无参构造方法」(PageResult());
@PutMapping 是 Spring MVC 提供的注解,核心作用是:标记接口只接收 HTTP PUT 类型的请求,且语义上对应「全量更新 / 替换资源」(比如修改用户的所有信息、更新订单的全部状态)。
@ConfigurationProperties,绑定配置文件中指定前缀的配置项到当前类的字段
@Configuration:告诉 Spring 这是一个「配置类」,类中的 @Bean 方法会被 Spring 扫描,方法返回的对象会被注册到 Spring 容器中
@ConditionalOnMissingBean,「条件注解」:只有 Spring 容器中没有 AliOssUtil 类型的 Bean 时,才执行这个方法;如果已有,则跳过,避免重复创建
@Transactional // 声明事务:方法内的数据库操作要么全成,要么全败
@Test 是 JUnit 框架(Java 单元测试标准框架)提供的注解 ,核心作用是:标记一个普通方法为「测试方法」,无需 main 方法,直接运行这个方法,验证代码逻辑是否符合预期(比如测试 Service 层的 saveWithFlavor 方法是否能正确新增菜品)。
| 注解 | HTTP 方法 | 核心语义 | 典型场景 |
|---|---|---|---|
| @GetMapping | GET | 查询资源 | 根据 ID 查用户、分页查订单 |
| @PostMapping | POST | 创建资源 | 新增用户、提交订单 |
| @PutMapping | PUT | 全量更新 / 替换资源 | 修改用户所有信息、更新订单 |
| @DeleteMapping | DELETE | 删除资源 | 删除用户、删除订单 |
service层的接口不加注解吗?只有service层的实现类加@Service注解?
Service 层的接口通常不加任何注解,只有实现类需要加 @Service 注解。
Service 接口的作用是「定义规范」(声明要实现的方法),它本身不包含任何业务逻辑,也不需要被 Spring 容器管理 ------Spring 管理的是「能干活的对象」(即接口的实现类),而不是「规范」。
给接口加注解导致两个问题:
- Spring 无法实例化接口(接口不能
new),启动时会报NoSuchBeanDefinitionException; - 违背「接口只定义规范」的设计原则,接口应该和容器解耦,专注于方法声明。
2.1.5 Tomcat
Tomcat 是一款开源的、轻量级的 Java Web 服务器 / 容器,核心作用是:运行基于 Java 开发的 Web 应用(比如你写的 Spring Boot 项目),接收前端浏览器 / 客户端的 HTTP 请求,处理后返回响应结果。
简单说:Tomcat 是 Java Web 应用的「运行容器」------ 你的 Spring Boot 代码(Controller/Service/Mapper)本身不能直接接收 HTTP 请求,必须把代码 "放进" Tomcat 里运行,Tomcat 负责和前端交互,调用你的代码逻辑。
Tomcat 的核心特征
- 核心定位:Servlet 容器(Java EE 规范实现,处理 HTTP 请求的 Java 类)
- 你的 Controller 层(比如
@RestController注解的类),底层本质是 Servlet; - Tomcat 负责管理 Servlet 的生命周期(创建、初始化、调用、销毁),处理请求的线程池、连接数等。
- 关键特性
| 特性 | 说明 |
|---|---|
| 轻量级 | 体积小、启动快,适合开发 / 测试 / 中小型项目(对比 WebLogic、JBoss 更轻便) |
| 开源免费 | Apache 基金会维护,无商业授权费用,是 Java Web 开发的标配 |
| 支持 HTTP/HTTPS | 直接处理 HTTP 请求(默认端口 8080),也可配置 HTTPS(443 端口) |
| 支持热部署 | 开发时修改代码后,无需重启 Tomcat 即可生效(IDEA 中常用) |
3.总结
- Tomcat 是「Java Web 服务器 / 容器」,核心作用是接收 HTTP 请求、运行 Java Web 应用、返回响应;
- Spring Boot 默认内置 Tomcat,启动项目时自动启动,无需手动配置;
- Tomcat 是 Java Web 开发的标配,相当于前端和后端代码之间的「中转站」。
2.1.6 反射机制
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
| 步骤 | 作用 | 关键 API |
|---|---|---|
| 获取 Class 对象 | 反射入口,代表类的元数据 | obj.getClass() / Class.forName() |
| 获取字段 | 拿到字段的反射对象 | getDeclaredField() / getField() |
| 开启可访问 | 突破访问权限 | setAccessible(true) |
| 读写字段 | 操作字段值 | get(obj) / set(obj, value) |
反射的特性
- 运行时类信息访问:反射机制允许程序在运行时获取类的完整结构信息,包括类名、包名、父类、实现的接口、构造函数、方法和字段等。
- 动态对象创建:可以使用反射API动态地创建对象实例,即使在编译时不知道具体的类名。这是通过Class类的newlnstance()方法或Constructor对象的newlnstance()方法实现的。
- 动态方法调用:可以在运行时动态地调用对象的方法,包括私有方法。这通过Method类的invoke()方法实现,允许你传入对象实例和参数值来执行方法。
- 访问和修改字段值:反射还允许程序在运行时访问和修改对象的字段值,即使是私有的。这是通过Field类的get()和set()方法完成的。
反射的核心入口:Class 类
Java 中所有类(比如 String、ArrayList、你自己写的 User 类)在运行时都会被 JVM 实例化为一个 Class 对象(注意大写 C),这个对象包含了该类的所有元数据(类名、字段、方法等)。
反射的应用:
- Spring:通过反射创建 Bean 对象、实现依赖注入;
- MyBatis:通过反射把数据库结果集映射为 Java 对象;
- IDE(Eclipse/IDEA):通过反射显示类的结构、自动补全代码;
- 序列化 / 反序列化(JSON 转对象):通过反射给对象的字段赋值。
反射的常用API
| 类 / 接口 | 作用 |
|---|---|
Class |
代表类的元数据(反射的入口) |
Field |
代表类的字段(成员变量) |
Method |
代表类的方法 |
Constructor |
代表类的构造器 |
一些核心方法:
getConstructor(参数类型...):只能获取公共(public) 的构造器;getDeclaredConstructor(参数类型...):能获取所有访问权限的构造器(包括私有);setAccessible(true):突破访问权限限制(反射的核心操作)。Field.set(对象, 值):给指定对象的该字段赋值;Field.get(对象):获取指定对象的该字段值(返回 Object,需强转)。Method.invoke(对象, 方法参数...):调用指定对象的该方法,参数按顺序传递;getMethod(方法名, 参数类型...):获取公共方法;getDeclaredMethod(方法名, 参数类型...):获取所有访问权限的方法。
Class<?> c = Class.forName(getName("className"));
这个
Class对象可以代表任意类的运行时类型,但我们暂时不知道(也不需要知道)具体是哪个类。
| 写法 | 含义 | 使用场景 |
|---|---|---|
Class<?> |
任意类的 Class 对象 | 动态加载类、通用类操作(如获取类名) |
Class<T> |
特定类 T 的 Class 对象 | 已知具体类型,需强类型约束(如反射创建 User 实例) |
Class |
裸类型(无泛型) | 不推荐使用(放弃类型检查) |
2.1.7 JAVA注解
注解(Annotation)是一种特殊的标记 ,可以标注在类、方法、字段、参数、包等代码元素上,格式是 @注解名(比如 @Override)。
注解本质 是一个继承了Annotation的特殊接口 ,其具体实现类是Java运行时生成的动态代理类 。
我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationlnvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
Java 自带了一批开箱即用的注解,分为两类:编译器注解 (给编译器看)和 元注解(给注解本身打标签)。
编译器注解
| 注解名 | 作用 | 示例 |
|---|---|---|
@Override |
标记方法重写父类的方法,编译器会检查方法签名是否正确(写错就报错) | java<br>@Override<br>public String toString() { return "test"; }<br> |
@Deprecated |
标记方法 / 类 / 字段已过时,使用时编译器会提示警告 | java<br>@Deprecated<br>public void oldMethod() {}<br> |
@SuppressWarnings |
抑制编译器警告(比如泛型未检查、过时 API 警告) | java<br>@SuppressWarnings("unchecked")<br>List list = new ArrayList();<br> |
@FunctionalInterface |
标记函数式接口(仅含一个抽象方法),编译器检查接口是否符合规范 | java<br>@FunctionalInterface<br>interface MyInterface { void doSomething(); }<br> |
元注解,元注解用于定义自定义注解的行为(比如注解能标在哪里、能保留多久),核心有 4 个:
| 元注解名 | 作用 |
|---|---|
@Target |
指定注解能标注的位置(比如类、方法、字段等),参数是 ElementType 枚举 |
@Retention |
指定注解的保留策略(生命周期),参数是 RetentionPolicy 枚举 |
@Documented |
标记注解会被 javadoc 工具生成到文档中 |
@Inherited |
标记注解能被子类继承(仅对类注解生效) |
@Target的ElementType常用值:TYPE:可标注类、接口、枚举;METHOD:可标注方法;FIELD:可标注字段;PARAMETER:可标注方法参数;CONSTRUCTOR:可标注构造器;ALL:所有位置。
@Retention的RetentionPolicy常用值:SOURCE:仅保留在源码中,编译后丢弃(比如@Override);CLASS:保留到编译后的 class 文件,但 JVM 运行时丢弃(默认值);RUNTIME:保留到运行时,可通过反射获取(框架常用,比如 Spring 的@Autowired)。
2.1.8 HttpClient
HttpClient是ApacheJakartaCommon下的子项目,可以用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。

HttpClient的核心API:
- HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
- HttpClients:可认为是构建器,可创建HttpClient对象。
- CloseableHttpClient:实现类,实现了HttpClient接口。
- HttpGet:Get方式请求类型。
- HttpPost:Post方式请求类型。
HttpClient发送请求步骤:
- 创建HttpClient对象
- 创建Http请求对象
- 调用HttpClient的execute方法发送请求
2.2 技术类
2.2.1 项目所涉及的技术栈
**SpringBoot:**快速构建Spring项目, 采用 "约定优于配置" 的思想, 简化Spring项目的配置开发。
SpringMVC:Spring MVC 是 Spring 框架中专门处理「前端和后端交互」的模块,核心作用是接收前端请求、处理请求、返回响应结果。SpringMVC是spring框架的一个模块,spring mvc和spring无需通过中间整合层进行整合,可以无缝集成。
Spring Task: 由Spring提供的定时任务框架。
**httpclient:**主要实现了对http请求的发送。
**Spring Cache:**由Spring提供的数据缓存框架
**JWT:**用于对应用程序上的用户进行身份验证的标记。
**阿里云OSS:**对象存储服务,在项目中主要存储文件,如图片等。
Swagger: 可以自动的帮助开发人员生成接口文档,并对接口进行测试。
**POI:**封装了对Excel表格的常用操作。
**WebSocket:**一种通信网络协议,使客户端和服务器之间的数据交换更加简单,用于项目的来单、催单功能实现。
MySQL: 关系型数据库, 本项目的核心业务数据都会采用MySQL进行存储。
Redis: 基于key-value格式存储的内存数据库, 访问速度快, 经常使用它做缓存。
Mybatis: 本项目持久层将会使用Mybatis开发。
**pagehelper:**分页插件。
**spring data redis:**简化java代码操作Redis的API。
**git:**版本控制工具, 在团队协作中, 使用该工具对项目中的代码进行管理。
**maven:**项目构建工具。
**junit:**单元测试工具,开发人员功能实现完毕后,需要通过junit对功能进行单元测试。
**postman:**接口测工具,模拟用户发起的各类HTTP请求,获取对应的响应结果。
2.2.2 Nginx
**反向代理:**隐藏后端服务地址,统一入口,让前端只访问 Nginx,不用关心后端在哪
**负载均衡:**当后端有多个服务器时,Nginx 把请求均匀分发到不同服务器,避免单点压力过大。
**静态资源:**直接处理 HTML/CSS/JS/ 图片等静态文件,不用让后端服务器处理,提升性能。
**动静分离:**把静态资源(前端)和动态请求(后端)分开处理,静态走 Nginx,动态走后端,提升整体性能。
**限流防护:**限制单位时间内的请求数,防止恶意攻击或突发流量打垮后端。
前端请求 → Nginx → (负载均衡)→ 后端服务1 / 后端服务2 / ...
↓
静态资源直接返回
2.2.3 ThreadLocal
ThreadLocal 并不是一个Thread,而是Thread的局部变量。 ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
-
public void set(T value) 设置当前线程的线程局部变量的值
-
public T get() 返回当前线程所对应的线程局部变量的值
-
public void remove() 移除当前线程的线程局部变量

2.2.4 AOP切面编程,实现功能增强,来完成公共字段自动填充功能
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
3). 在 Mapper 的方法上加入 AutoFill 注解
1)自定义注解 AutoFil
java
package com.sky.annotation;
import com.sky.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:UPDATE INSERT
OperationType value();
}
| 元注解 | 取值 | 含义 |
|---|---|---|
| @Target | ElementType.METHOD | 注解只能加在方法上 |
| @Retention | RetentionPolicy.RUNTIME | 注解保留到运行时,可通过反射获取(插件核心依赖) |
2)自定义切面 AutoFillAspect
java
package com.sky.aspect;
import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
/**
* 自定义切面,实现公共字段自动填充处理逻辑
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
/**
* 前置通知,在通知中进行公共字段的赋值
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始进行公共字段自动填充...");
//获取到当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = autoFill.value();//获得数据库操作类型
//获取到当前被拦截的方法的参数--实体对象
Object[] args = joinPoint.getArgs();
if(args == null || args.length == 0){
return;
}
Object entity = args[0];
//准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型,为对应的属性通过反射来赋值
if(operationType == OperationType.INSERT){
//为4个公共字段赋值
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象属性赋值
setCreateTime.invoke(entity,now);
setCreateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}else if(operationType == OperationType.UPDATE){
//为2个公共字段赋值
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象属性赋值
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
AOP的核心概念
| 术语 | 含义 | 对应本类中的代码 |
|---|---|---|
| 切面(Aspect) | 封装通用逻辑的类(比如这个 AutoFillAspect) | @Aspect 注解标记的类 |
| 切入点(Pointcut) | 定义要拦截哪些方法 | @Pointcut(...) 注解的方法 |
| 通知(Advice) | 拦截方法后要执行的逻辑(前置 / 后置 / 环绕等) | @Before(...) 注解的 autoFill 方法 |
| 连接点(JoinPoint) | 被拦截的方法(比如 Mapper 的 insert/update 方法) | joinPoint 参数 |
3)在Mapper接口的方法上加入 AutoFill 注解
以CategoryMapper 为例,分别在新增和修改方法添加@AutoFill()注解,也需要EmployeeMapper做相同操作
java
package com.sky.mapper;
@Mapper
public interface CategoryMapper {
/**
* 插入数据
* @param category
*/
@Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)" +
" VALUES" +
" (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
@AutoFill(value = OperationType.INSERT)
void insert(Category category);
/**
* 根据id修改分类
* @param category
*/
@AutoFill(value = OperationType.UPDATE)
void update(Category category);
}
2.2.5 JAVA异常

2.2.6 关于DTO,VO和实体之间的关系。
DTO、VO、Entity 是数据载体类的核心概念,它们各司其职,解决 "不同层级之间数据传递、数据展示、数据存储" 的问题。
1. 实体(Entity): 数据库的 "镜像",对应数据库中的一张表,每个字段对应表的列,是数据持久化的核心载体。
Entity 就是 "数据库表在代码里的化身",字段和表列一一对应,只负责和数据库交互,不包含业务逻辑。只关注 "数据存储",不关注 "前端展示" 或 "接口传参"。
2. DTO(Data Transfer Object): 数据传输对象。用于不同层级 / 系统之间传递数据的对象(比如前端→后端、服务 A→服务 B),按需封装字段,避免传输冗余数据。
DTO 是 "数据的快递包裹",你需要传什么数据,就往包裹里装什么,不需要的字段一律不放,减少网络 / 内存开销。
3. VO(View Object): 视图对象。用于向前端展示数据的对象,是 "前端能看懂的格式",可能包含多个 Entity/DTO 的数据,或做格式转换。
VO 是 "给前端的最终展示模板",比如前端需要 "订单时间显示为 yyyy-MM-dd"、"订单状态显示为中文(待付款 / 已完成)",这些都在 VO 里处理。
