架构
mvc

1 | your-project/ |
- Model 负责数据和业务逻辑
- View 负责界面显示
- Controller 负责处理请求和协调Model与View
1 | 用户点击按钮 |
拦截器
SpringCloud
远程调用,注册中心,网关都是在单体项目变成分布式项目后对出现的问题的解决.
远程调用:
分布式项目 数据库只能连接一个项目,不同项目之间有耦合, 所以要远程调用
注册中心:分布式项目有多个服务器,端口写死维护困难,所以要注册中心
网关:分布式项目因为有不同项目,前端只发一个json.需要网关创建路由,就知道访问哪里了.
远程调用:拆分成很多单体项目,不同项目之间通过 http请求 完成数据业务交换

Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。
注册RestTemplate到Spring容器
调用RestTemplate的API发送请求,常见方法有:
getForObject:发送Get请求并返回指定类型对象
PostForObject:发送Post请求并返回指定类型对象
put:发送PUT请求
delete:发送Delete请求
exchange:发送任意类型请求,返回ResponseEntity

可以用OpenFeign + Nacos简化开发

连接池(OK HTTP)
因为可能出现不同服务需要同样的Client,导致重复编码,所以可以抽取相同的Client

注册中心(nacos)

服务提供方可能不止一个服务器,
这时就需要有一个注册中心为服务消费方找到要调用的端口
远程调用(openfeign)
使用步骤:
在服务消费者里引入 spring-cloud-starter-openfeign。
在启动类加 @EnableFeignClients。
定义一个接口,写上 @FeignClient(name = “服务名”),就能像本地方法一样调用远程服务。
负载均衡
DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用
心跳机制
网关
路由、转发、身份验证
可以从注册中心拉取端口
前端->网关->注册中心->分布式后端
解决前端对微服务难以维护,有了网关,前端就可以像单体式架构一样直接调用网关了
但是!!后端开发仍然需要配置路由规则,让json找到需要的服务
路由
Gateway 收到请求
请求被包装成一个 ServerWebExchange 对象。
路由匹配阶段
Gateway 遍历所有 Route。
调用每个 Route 的 predicate。
这里的 Path=/api/user/** 断言返回 true → 匹配成功。
路由命中
- Gateway 确定目标 URI 为 lb://user-service。
- 创建过滤器链(包括全局和局部过滤器)。
执行前置过滤器(pre)
- StripPrefix=1 会将路径从 /api/user/list 变成 /user/list。
- 然后继续。
请求被转发
- Gateway 将请求交给负载均衡器(LoadBalancerClient)。
- 实际转发给一个实例,比如 http://192.168.0.5:8081/user/list。
微服务处理并返回响应
- user-service 返回 JSON 数据。
执行后置过滤器(post)
- 比如加响应头、记录日志等。
Gateway 将结果返回客户端
路由断言:规定请求是否命中某条路由
路由过滤器:请求转发前后对请求/响应做修改、增强或拦截

网关登陆校验的原理就是在pre设置一个过滤器,就可以在网关转发之前做校验
自定义过滤器
登录校验

1.gateway -> 微服务:
修改登录校验拦截器的处理逻辑,保存用户到请求头
hm-common中拦截器保存登录用户,保存到ThreadLocal
拦截器应该只在微服务中,gateway可以用SpringMVC来排除
2.微服务 -> 微服务:

通过OpenFeign在hm-api的拦截器传递用户信息

配置管理(Nacos)
微服务重复配置过多,维护成本高
业务配置经常变动,每次修改都要重启
服务网关路由配置写死,如果变更要重启网关
配置共享

配置热更新

TODO 动态路由
微服务保护(雪崩)
1.请求限流
2.线程隔离
3.服务熔断
4.失败处理fallback
Sentinel
分布式事务
微服务下,事务回滚无法完成
seata
高并发的解决方法
XA 模式(数据库层两阶段)
事务锁,强一致性
AT 模式(Seata UndoLog)
TCC 模式(业务层补偿)
异步通讯
作用:
1.异步
2.解耦
RabbitMQ

交换机(Exchange)
Fanout:广播,将消息交给所有绑定到交换机的队列。
Direct:订阅,基于RoutingKey(路由key)发送给订阅了消息的队列
Topic:通配符订阅,与Direct类似,只不过RoutingKey可以使用通配符
Headers:头匹配,基于MQ的消息头匹配,用的较少。
s-pay-mall业务流程
类的静态方法没法设置对象状态.可以用类生成对象,设置对象状态.这就是实例化.
每次都要实例化有 多层嵌套、销毁、多实例 的麻烦.所以IoC通过:
- 让类进入容器: 类加@Component,或者配置类加@Bean
- 给属性赋值: 基本类型和String 使用 @Value , 其他的 使用 @Autowired (注入方式多用构造函数)
- 调用动态方法
此外,IoC的优势有:
IOC 容器是 AOP 的基础.通过容器管理 Bean,可以在 Bean 方法执行前后加增强(事务、日志、权限校验).
如果对象不在容器里,就无法自动代理和增强
登录校验
商品交易
1 | graph TD |
具体代码分析
鉴权
请求示例:
1 | timestamp = 1000000 |
解码 token在网站设置
1 | SignatureUtil.check(token, signature, timestamp, nonce) |
为什么需要同时使用 timestamp 与 nonce?
随机数 nonce :保证请求在并发场景下唯一
nonce 的意义在于避免签名冲突。
在实际系统中,同一秒内可能出现大量并发请求。
如果没有 nonce,所有同一秒内的请求会共享相同的 timestamp,导致 signature 可能重复。
因此 nonce 用于确保:
- 同一秒内的请求也具有不同签名;
- 内容相同的多个请求仍能被区分。
需要注意的是:
nonce 本身不包含时效性。攻击者可以截获一次合法请求(含 nonce),在之后任意时间重新发送。如果服务器不记录 nonce,将无法分辨这是旧请求。
时间戳 timestamp :判断请求是否在有效期内
即便 nonce 很随机,但无法判断该请求是“刚刚发送的”还是“很久以前的”。
timestamp 则解决了这一问题。
服务器常见的做法是:
- 若 timestamp 与当前时间差距过大(例如超过五分钟),则认为该请求为过期请求并拒绝处理。
因此 timestamp 的核心作用是防止重放攻击,即阻止攻击者对旧请求进行重复发送。
为什么不能只使用 timestamp
timestamp 精度通常为“秒”。
在实际业务中,同一秒可能出现多条请求。
若只依赖 timestamp,将无法区分同一秒内的多条合法请求,从而可能导致签名冲突。
nonce 的存在就是为了解决这一问题。
- 公众号的事件推送常出现并发;
- 同一秒内可能由多名用户触发多条消息;
- 官方服务器可能批量推送多条记录。
因此,真实环境中无法依赖 timestamp 单独提供区分能力,所以 nonce 必须保留。
登陆

开发者设置 Token 固定 验证微信服务器调用你接口的合法性 永久
access_token 会变 调用微信 API(发消息、生成二维码等) 2 小时
1 | private static final String BASE_URL = "https://api.weixin.qq.com/"; |
这个@Bean的作用是:
把一个配置好的 Retrofit 客户端交给 Spring 容器管理,然后可以在项目中通过依赖注入直接使用。
1 | @Autowired |
实例化:
- 依赖对象状态(比如 base URL、converter、token 等)。
- 需要先生成实例,才能调用方法。
- 调用实例方法时,Retrofit 内部会生成 HTTP 请求并发出去。
问题1:
根据你之前提供的环境:
- IDEA 2025.1
- JDK 1.8
- Maven 3.8.x
- Lombok 1.18.30
这种组合在某些 JDK 1.8 上仍会触发 TypeTag :: UNKNOWN。原因主要是 Javac 内部版本过旧,无法正确解析枚举/Builder。