第一章 电商秒杀商品回顾

项目环境及技术

学习环境:

  • Intellij IDEA 2021.3
  • 阿里云ECS或本地Linux虚拟机,操作系统 centos 7.6
  • MySQL5.6数据库,Redis4.0.1缓存,消息队列rocketmq4.5,phantomjs无头浏览器

技术储备:

  • 了解 SSM、SpringBoot 等框架
  • 熟悉 Linux 基本命令
  • 了解 MySQL 常用命令
  • 了解 Redis 常用命令

项目框架设计

第一章 电商秒杀商品回顾_redis缓存

数据层:

  • 事务 @Transcantional 注解的处理方式—表示该方法处于一个事务当中,若一个事务中有任何一个步骤失败,事务就会回滚
  • 数据接入层数据 Dao
  • 本地缓存、集中式缓存在商品详情页的应用,提高流式读取的效率

大概流程

  • 整个页面基于HTML、CSS,然后基于JavaScript的jQuery库发送了一个动态交互的请求,给接入层controller进行通用处理,然后我们基于SpringMVC的controller层会向业务层调用相应的服务;
  • 业务层会调用数据层的Dao,通过事务管理数据 DaoMapper 的方式将数据的增删改查落入到数据库中,最后到本地电脑中。

第一章 电商秒杀商品回顾_跨域请求_02

  • 数据模型(Data Object):借助于Mybatis的ORM操作将关系型数据库的表结构,通过XML的方式,定义成Java的Object结构。
  • 领域模型(Domain Model):具有一个对象的生命周期(创建、更新、删除、消亡),它可以和数据模型组合,比如用户对象是一个领域模型,它是由用户基本信息+用户密码信息两个数据模型共同组成的。
  • 贫血模型:项目里的用户对象就设计成贫血模型:指的是拥有各种属性信息和get、set方法,但是不提供登陆、注册等功能。
  • ViewObject:与前端对接的模型,供展示的聚合模型。

项目详细设计

第一章 电商秒杀商品回顾_springboot_03

第一章 电商秒杀商品回顾_跨域请求_04

问题

为什么要将商品的库存表item_stock与商品表item分开呢 ?

  • 库存操作非常耗时、性能效率低,在商品交易过程中库存减,如果合并到item表中,每次d都会对对应的行加行锁。
  • 如果分开库存表,虽然每次减库存过程还是会加行锁,但是可以将这张表拆开到另一个数据库当中,分库分表,做效果的优化。

跨域请求问题

  • 在存在跨域请求问题的类前都加上一句支持跨域操作(服务端解决方式):
@CrossOrigin(origins = {"*"}, allowCredentials = "true")

第一章 电商秒杀商品回顾_springboot_05

  • 问题出现在:由于我们是做前后端分离的设计,jQuery 会有跨域限制,用 ajax 请求对应网页的 url 文件位置时,静态资源文件和 jQuery 动态请求是分开部署的,所以在ajax请求要加上一句:
xhrFields:{withCredentials:true},
  • 因为要解决客户端上 session 共享的问题(客户端 session 共享)

第一章 电商秒杀商品回顾_mybatis_06

另外,像 Safari 浏览器需要关闭​​阻止跨站跟踪​​​和​​阻止所有Cookie​​ 的选项,才能正常使用跨域请求

第一章 电商秒杀商品回顾_redis缓存_07

跨域感知 session

  • 跨域感知 session 需要解决两个问题,第一个是解决跨域问题,第二个是解决跨域 cookie 传输问题

跨域问题

  • 解决跨域问题就如上面所示,使用了SpringBoot自带的​​@crossOrigin​​ 注解
@CrossOrigin(origins = {"*"}, allowCredentials = "true")
  • 注解加上后,所有的 http response 头上都会加上​​Access-Control-Allow-Origin *​​​ 以及​​Access-Control-Allow-Headers *​​ 两个头部,这样可以满足CORS的跨域定义,我们的ajax 看到这两个头部就认定对应的域名接收任何来自或不来自于本域的请求

跨域传递 cookie 问题

  • 跨域和跨域传递 cookie 是两个不同纬度的问题,我们依靠上述的方式解决了跨域问题,但要做到跨域感知 seesion 需要解决在跨域的前提下将 cookie 也能传上去,这时候就需要设置另外一个头部,我们的​​@CrossOrigin​​ 变成
@CrossOrigin(origins = {"*"}, allowCredentials = "true", allowedHeaders = "*")
  • 使用了​​allowCredentials​​​ 后​​Access-Control-Allow-Credentials​​​ 头被设置成​​true​​​,同时前端设置​​xhrField:{withCredential:true}​​​ 后,浏览器在 ajax 请求内带上对应的​​cookie​​​ 头部和后端的​​allowCredentials​​ 配合在一起解决跨域传递cookie的问题。
  • 由于课程中仅仅使用了get 和 post 的方法,而这两个方法在跨域请求中都是可以用的,因此​​allowedHeaders​​ 可以不加。

全局异常处理器 404,405 问题

  • 404 问题就是页面URL访问不到,之前异常处理的方式是定义BaseController层捕获,但是对于没有进入Controller层的请求就无法处理。
  • 405 问题就是比如该传的url参数没有传,url绑定路由问题。

解决方法

  • 定义 GlobalExceptionHandler,类前添加注释 @ControllerAdvice 面向切面编程。
  • 在配置文件​​application.properties​​​ 中添加​​spring.mvc.throw-exception-if-no-handler-found=true​​​ 和​​spring.resources.add-mappings=false​

第一章 电商秒杀商品回顾_redis缓存_08

// 异常处理类
@ControllerAdvice
public class GlobalExceptionHandler {

// controller层抛出异常后都会被doError方法所捕获并根据异常类型进行不同的判断
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonReturnType doError(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Exception ex) {
ex.printStackTrace();
Map<String, Object> responseData = new HashMap<>();
if (ex instanceof BusinessException) {
BusinessException businessException = (BusinessException) ex;
responseData.put("errCode", businessException.getErrCode());
responseData.put("errMsg", businessException.getErrMsg());
} else if (ex instanceof ServletRequestBindingException) {
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
responseData.put("errMsg", "url绑定路由问题");
} else if (ex instanceof NoHandlerFoundException) {
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
responseData.put("errMsg", "没有找到对应的访问路径");
} else {
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
responseData.put("errMsg", EmBusinessError.UNKNOWN_ERROR.getErrMsg());
}
return CommonReturnType.create(responseData, "fail");
}

}