未整理的文件
问题
关于AOP
如果你在技术派的源码中搜 MdcUtil 的话,可以在 ReqRecordFilter 中找得到,顾名思义,该类是对请求的一个过滤器,会在每个请求中加上全链路的 traceid。
项目中一整个MdcDot注解都被用来记录方法用时,那么reqrecord中的记录又是在干什么?他有一个.log文件的记录,同时还把这一次的请求时间输出了。
我想一个适用于普通方法,而req的这个是在filter里专门记录请求的时间,可能和那个方法不兼容?
还有一个问题,两个方法都用的mdcutil里的方法,这个traceid没有覆盖的可能吗?
1. MdcDot 与 ReqRecord 的区别?traceId 会不会互相覆盖?
你已经大致猜对了,但我帮你补充成一个完整逻辑:
1)MdcDot 用来记录方法执行耗时
- 这是一个 AOP 注解(作用在某个方法上)
- AOP 拦截方法 → 进入前记录时间 → 退出后输出日志
- 一般属于业务级别的方法耗时监控(精确到某个 service 方法)
例如:
UserService.getUserById() 花了 230ms
OrderService.create() 花了 1.2s这些属于应用内部的性能监控。
2)ReqRecord(你看到的 req 处理)是 Filter 层的请求级日志
这段逻辑在 Filter 或 Interceptor 里:
- 进入请求:建立 traceId、记录用户信息、记录路径、IP、UA
- 完成请求:统一输出一次完整请求日志(写入 .log)
这是每次 HTTP 请求的入口 / 出口监控。
也就是“这次 API 调用持续了多久”。例如:
GET /api/user/info cost=156ms
POST /order/create cost=1220ms该日志是更偏向运维监控、流量统计、接口质量监控。
3)两个都用 MdcUtil,会不会覆盖 traceId?
不会。
原因:
- traceId 是在 filter 入口时统一设置的
- AOP 方法执行时只会使用这个 traceId,不会重新生成
- MdcUtil.addTraceId() 基本格式就是:“如果 MDC 中没有 traceId 才生成”
所以一个请求有一个 traceId,所有方法耗时都共享它。
这个项目细分了好多类,他还有一个Dao,专门和service区分开来了。@Respository和@Service 的区别只是标记不一样吗
2. Repository 与 Service 的区别?什么时候分层?
不是标签不同而已。
Repository(DAO)职责
只负责与数据库交互:
- CRUD
- 自定义 SQL
- 查询分页
- 统计 SQL
Repository/mapper 层不包含任何业务逻辑。
Service 的职责
封装业务逻辑:
- 参数校验
- 领域逻辑组合
- 调用多个 DAO / RPC
- 事务控制
- 缓存控制
什么时候需要分?
当你有以下需求时必须分:
- 业务需要组合多个数据库表的操作
- 一个请求涉及多个操作(比如先查用户再查订单)
- 需要保证事务(@Transactional)
- 需要隐藏数据库细节(controller 不应该直接写 SQL)
你之前的项目没有 DAO 层,是因为业务太简单。
所以说,之前那个项目有service层用其他service的情况,这种情况其实应该使用dao吗?
用service CRUD 和Mapper CRUD 的区别在?本来serviceimpl这个继承的类里就要求mapper继承了basemapper的,那用哪个有说法吗?我想了想可能是因为:用service的肯定是controller 层 ,controller层不可能去调用mapper层的东西的,即使调用的service其实本质上还是mapper,而dao(service)层就可以做基本CURD做不到的事情?
3. 为什么 MyBatis-Plus 要提供 Service CRUD?和直接在 Service 调 Mapper 有什么区别?
你问的这个问题非常关键:
“既然 Controller 能调 Service,那为什么 Service 不自己写 CRUD,而要依赖 MP 提供的 ServiceImpl 的 CRUD?”我拆成 3 点,你看了就懂了。
① MyBatis-Plus 的 Service CRUD 是通用业务服务层的模板
MP 的设计理念:
- Mapper 层只负责数据库 CRUD(原子、低级别操作)
- Service 层需要在 Mapper 的基础上补充业务流程逻辑
- 绝大多数业务都会需要标准化的 CRUD(分页、按条件查询、批量删除……)
因此 MP 提供了 IService + ServiceImpl,目的是:
- 你不用每个项目都重复写:list、getById、removeById、save、update……
- 这些方法自动配合 MP 的 QueryWrapper、分页插件、自动填充等
- 代码统一规范、减少重复性工作
也就是说,它做的是“中间层简化”和“业务层能力增强”。
② 你完全可以不继承 IService,但你会多写大量重复代码
假设你不用 MyBatis-Plus 的 IService,你会写成这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class UserService {
@Autowired
private UserMapper mapper;
public User getById(Long id) {
return mapper.selectById(id);
}
public List<User> list() {
return mapper.selectList(null);
}
public void save(User u) {
mapper.insert(u);
}
public void update(User u) {
mapper.updateById(u);
}
public void delete(Long id) {
mapper.deleteById(id);
}
}这是一堆重复工作,而且不同开发者写出的接口风格可能完全不一致。
MP 帮你统一成:
1
2@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}然后你可以直接用:
1
2
3
4userService.getById(id);
userService.list();
userService.save(entity);
userService.removeById(id);你几乎不用写额外代码,即可保证所有项目统一风格。
为什么这里要用@Param注释?不加不可以吗?这是mybtis就有的还是plus才有的?这样的话在yml设置里开启驼峰命名的意义在?
1
2
3
4
5
6
7
8
9
10public interface UserMapper extends BaseMapper<UserDO> {
/**
* 根据三方唯一id进行查询
*
* @param accountId
* @return
*/
UserDO getByThirdAccountId( String accountId);
}情况1:编译器保留了参数名(默认情况自 JDK8 后大多数 IDE 会开启)
MyBatis 可以直接识别:
1
UserDO getByThirdAccountId(String accountId);
此时可以写:
1
#{accountId}
无需 @Param。
情况2:项目没有启用参数名保留功能(某些环境未开启 -parameters)
MyBatis 拿到的参数名会变成:
1
arg0, arg1, arg2...
你的方法:
1
UserDO getByThirdAccountId(String accountId)
在运行期可能变成:
1
arg0
这时你 SQL 写:
1
#{accountId}
就会报错:找不到 accountId。
==> 因此为了防止环境不一致,团队规范通常建议:多参数和注解 SQL 要使用 @Param。
TableId(type = IdType.AUTO),在数据库本来就设置了自增的情况下,还用这个注解让他在插入的时候自增其实没什么意义吧?哦,等等,它的数据库都没有设置约束,约束全在mybatis里,这样的好处在?
5. @TableId(type = IdType.AUTO) 为何数据库不建约束?
MP 默认通过注解控制主键策略,数据库里不建约束有两个好处:
1)跨数据库兼容
MySQL, PostgreSQL, Oracle 都有不同的自增写法
MP 用注解帮你屏蔽差异。2)方便迁移(比如切换到雪花算法)
你只改 Java 注解,不需要改表结构。
项目越大越喜欢用 Java 控制,而非 DBA 控制。
为何这个是用的传统渲染页面?
```java
@Controller
public class IndexController extends BaseViewController {@Autowired private IndexRecommendHelper indexRecommendHelper; @GetMapping(path = {"/", "", "/index", "/login"}) public String index(Model model, HttpServletRequest request) { String activeTab = request.getParameter("category"); IndexVo vo = indexRecommendHelper.buildIndexVo(activeTab); model.addAttribute("vo", vo); return "views/home/index"; }}
1
2
3
4
5
6
7
8
9
> ## 为什么不直接在 Controller 调 Service?
>
> 这是你问得最**专业**的点。
>
> ### ❌ 反模式(Controller 直接堆 Service)
>ArticleService
TagService
UserService
RecommendService
…1
2
3
4
5
6
7
8
9
10
11
12
Controller 会变成:
- 巨大
- 难维护
- 逻辑混乱
- 改一处牵一堆
------
### ✅ 这里采用的是「Helper / Facade 层」Controller
↓
IndexRecommendHelper ← 聚合逻辑
↓
多个 Service1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
`IndexRecommendHelper` 的职责是:
> **“为某一个页面 / 场景,组装完整的业务数据”**
------
## 五、这样设计的好处(非常重要)
### 1️⃣ Controller 极度干净
Controller 只做 3 件事:
1. 接收请求
2. 调用场景服务
3. 返回视图
------
### 2️⃣ Service 保持“单一业务职责”
- ArticleService:文章
- TagService:标签
- UserService:用户
不污染 Service 层
------
### 3️⃣ 页面级逻辑可以复用
今天是:buildIndexVo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
明天可能是:
- PC 首页
- 移动端首页
- 小程序首页
**Helper 可以复用,Controller 各自不同**
------
### 4️⃣ 非常适合「复杂页面」
首页通常是:
- 数据多
- 来源多
- 规则多
- 经常改
Helper / Facade 是最合理的位置。
------
## 六、你可以这样理解它的角色
| 层级 | 角色 |
| --------------- | ------------------ |
| Controller | HTTP + 路由 + 视图 |
| Helper / Facade | 页面级业务编排 |
| Service | 单一领域业务 |
| VO | 页面数据模型 |
------
## 七、一句话总结(面试级)
> 这段代码是一个 **MVC 页面 Controller**,
> 它不直接调用 Service,而是通过 Helper 聚合首页所需的业务数据,
> 最终将 VO 注入 Model 并返回视图路径。
> 这种设计降低了 Controller 复杂度,提高了页面逻辑的可维护性和复用性。
Spring MVC 的流程是:Controller
↓ 返回 “views/home/index”
ViewResolver
↓ 拼接前缀 + 后缀
模板引擎(Thymeleaf / JSP)
↓ 渲染 HTML
浏览器收到 HTML1
2
3
例如(常见配置):spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html1
2
3
那么:return “views/home/index”;
1
2
3
最终渲染的是:classpath:/templates/views/home/index.html
1
2
3
4
5
## 1️⃣ template.execute(...) —— 为什么要这么写?return template.execute(new RedisCallback
- >>() {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
### 这是在干什么?
- `template` 是 `RedisTemplate`
- `execute` 允许你:
- **直接拿到底层 RedisConnection**
- 使用 Redis 的原生命令(性能更好、功能更全)
### 为什么不用 `redisTemplate.opsForZSet()`?
因为:
- `zRangeWithScores` 在某些版本/泛型下不好用
- 直接用 `RedisConnection` **最稳定、最贴近 Redis 原生命令**
👉 **这是偏“底层工具类”的写法**
| 数据结构 | 核心能力 | 最典型用途 |
| -------- | ----------- | -------------- |
| String | 单值 / 计数 | 缓存、计数、锁 |
| List | 有序 | 队列、历史 |
| Set | 去重 | 白名单、点赞 |
| ZSet | 排序 | 排行榜 |
| Hash | 结构化 | 对象缓存 |
```java
@Test
public void testExecute() {
// 使用 execute 方法执行 Redis 命令
redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
// 执行 Redis 命令,例如 set 和 get 命令
connection.set("itwanger".getBytes(), "沉默王二".getBytes());
byte[] value = connection.get("itwanger".getBytes());
String strValue = new String(value);
// 输出获取到的值
System.out.println(strValue);
return null;
}
});
}
我草吓哭了
- 构建工具:后端(Maven、Gradle)、前端(Webpack、Vite)
- 单元测试:Junit
- 开发框架:SpringMVC、Spring、Spring Boot
- Web 服务器:Tomcat、Caddy、Nginx
- 微服务:Spring Cloud
- 数据层:JPA、MyBatis、MyBatis-Plus
- 模板引擎:thymeleaf
- 容器:Docker(镜像仓库服务 Harbor、图形化工具 Portainer)、k8s、Podman
- 分布式 RPC 框架:Dubbo
- 消息队列:Kafka(图形化工具 Eagle)、RocketMQ、RabbitMQ、Pulsar
- 持续集成:Jenkins、Drone
- 压力测试:Jmeter
- 数据库:MySQL(数据库中间件 Gaea、同步数据 canal、数据库迁移工具 Flyway)
- 缓存:Redis(增强模块 RedisMod、ORM 框架 RedisOM)
- nosql:MongoDB
- 对象存储服务:minio
- 日志:Log4j、Logback、SF4J、Log4j2
- 搜索引擎:ES
- 日志收集:ELK(日志采集器 Filebeat)、EFK(Fluentd)、LPG(Loki+Promtail+Grafana)
- 大数据:Spark、Hadoop、HBase、Hive、Storm、Flink
- 分布式应用程序协调:Zookeeper
- token 管理:jwt(nimbus-jose-jwt)
- 诊断工具:arthas
- 安全框架:Shiro、SpringSecurity
- 权限框架:Keycloak、Sa-Token
- JSON 处理:fastjson2、Jackson、Gson
- office 文档操作:EasyPoi、EasyExcel
- 文件预览:kkFileView
- 属性映射:mapStruct
- Java 硬件信息库:oshi
- Java 连接 SSH 服务器:ganymed
- 接口文档:Swagger-ui、Knife4j、Spring Doc、Torna、YApi
- 任务调度框架:Spring Task、Quartz、PowerJob、XXL-Job
- Git 服务:Gogs
- 低代码:LowCodeEngine、Yao、Erupt、magic-api
- API 网关:Gateway、Zuul、apisix
- 数据可视化(Business Intelligence,也就是 BI):DataEase、Metabase
- 项目文档:Hexo、VuePress
- 应用监控:SpringBoot Admin、Grafana、SkyWalking、Elastic APM
- 注解:lombok
- jdbc 连接池:Druid
- Java 工具包:hutool、Guava
- 数据检查:hibernate validator
- 代码生成器:Mybatis generator
- Web 自动化测试:selenium
- HTTP 客户端工具:Retrofit
- 脚手架:sa-plus
?