电商秒杀系统框架回顾
- 项目简介
- 外部依赖
- 框架回顾
- 项目要点
- 项目中存在的问题
- 小结
课程是免费的,课程地址如下:SpringBoot搭建电商秒杀项目,课程真的很棒,作者的思路很清晰,建议各位读者可以跟着视频练习一下这个项目;
项目简介
通过SpringBoot快速搭建的前后端分离的电商基础秒杀项目。项目通过应用领域驱动型的分层模型设计方式去完成:用户otp注册、登陆、查看、商品列表、进入商品详情以及倒计时秒杀开始后下单购买的基本流程;
架构图如下:
外部依赖
- org.springframework.boot:spring-boot-starter-web
- mysql:mysql-connector-java
- com.alibaba:druid
- org.mybatis.spring.boot:mybatis-spring-boot-starter
- org.apache.commons:commons-lang3
- org.hibernate:hibernate-validator
- joda-time:joda-time
- junit:junit
- org.springframework:spring-test
- org.mybatis.generator:mybatis-generator-maven-plugin (插件)
框架回顾
Spring Boot 对应的配置化操作,只需要在resources目录下创建默认配置文件——application.yml或application.properties,在其中进行配置
集成Mybatis:进入pom文件,确定使用的数据库,使用mysql,则引入mysql-connector-java包;确定使用什么连接池来管理mysql的链接,这里使用阿里巴巴的druid连接池。然后将spring boot对mybatis的支持引入,这里使用mybatis-spring-boot-starter包;在配置文件类导入mybatis需要的一些配置,用来启动一个带mybatis数据库访问的一个spring boot工程;然后使用mybatis的自动生成工具,用来生成对应数据库文件的映射;
dao层与dataobject层由mybatis自动生成工具生成,dataobject层(负责数据存储到service层的传输)下的类对应数据库对象模型,其中的字段与数据库一一映射,dao层下的类定义对数据库进行交互的方法,在resources的mapping目录下有自动生成dao层对应的配置文件,一起实现对数据库的操作。
在service层下的model目录中创建对象模型(不可以把数据库的映射简单透传返回给想要service的服务,这个model对应Spring MVC中业务逻辑交互的模型),然后在数据库中创建对应的表,在service层下创建对应对象的service接口,接口中定义需要的方法(方法的返回类型大部分为model对象),在service层下impl目录下去实现。添加方法:在mapping目录下对应配置文件中添加相应的SQL语句,在dao目录下的对应接口中建立映射(添加方法)。
在serviceImpl中定义数据库对象dataobject与模型对象model相互转换的方法,在controller中定义模型对象model与视图对象viewobject相互转换的方法,这里使用到了org.springframework.beans.BeanUtils方法;serviceImpl类与controller类中分别需要添加@Service注解和@Controller注解
response层定义了统一返回类型,有一个通用对象,最终返回前端的对象为该通用对象
error层有一个common error接口,一个Enum枚举类实现该接口,该枚举类通过构造方法构造一个实现common error接口的enum类型的子类,一个继承Exception且实现common error方法的类,其内强关联一个对应的common error(即enum类),且实现其对应构造函数以方便使用。该方式对应设计模式——包装器业务异常类实现。
在controller层下中的viewobject目录下创建可供UI使用的对象,其中的字段为可以给用户查看的字段。在controller层下创建对应对象的controller类,在其中调用其service接口中的方法来实现相应逻辑;
1
2
3
4
5
6
7
8 1@ExceptionHandler(Exception.class)//需要指明收到什么样的exception之后才会进入它的处理环节,此处定义为根类
2@ResponseStatus(HttpStatus.OK)//捕获到controller抛出的exception,并返回HttpStatus.OK,即status=200
3@ResponseBody //handler exception使用这种方式(Object会寻找本地页面文件)仅仅只能返回页面路径,无法处理viewobject类对应的@ResponseBody形式,加上@ResponseBody注解即可解决
4public Object handlerException(HttpServletRequest request, Exception ex){
5 //获取异常并处理
6}
7
8
其他所有controller类都要继承基类BaseController
在UserController中,用户登录接口中将登陆凭证加入到用户登录成功的session中;
1
2
3
4
5
6
7
8
9
10
11
12
13 1@Autowired
2private HttpServletRequest httpServletRequest;
3//通过bean的方式注入进来,代表这个HttpServletRequest是单例模式
4//单例模式怎么可以支持一个request支持多个用户的并发访问?
5//bean包装的HttpServletRequest,本质是一个proxy,它内部拥有ThreadLocal方式的map,去让用户在每个线程当中去处理它自己对应的request,
6//并且有ThreadLocal清除机制,可以放心使用,且这个HttpServletRequest对应当前用户的http请求
7
8
9//将登陆凭证加入到用户登录成功的session中,分布式中用token
10this.httpServletRequest.getSession().setAttribute("IS_LOGIN",true);
11this.httpServletRequest.getSession().setAttribute("LOGIN_USER",userVO);
12
13
登录密码加密:
因为jdk自带的MD5实现的方式只支持16位MD5,更改加密方法;
1
2
3
4
5
6
7
8
9
10
11 1//将密码加密
2public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
3 //确定计算方法
4 MessageDigest md5 = MessageDigest.getInstance("MD5");
5 BASE64Encoder base64Encoder = new BASE64Encoder();
6 //加密字符串
7 String newstr = base64Encoder.encode(md5.digest(str.getBytes("utf-8")));
8 return newstr;
9}
10
11
validator层下为格式化校验规则,在pom文件中引入Apache Commons Lang包,使用了其StringUtils工具类;还引入了hibernate-validator包来进行校验。其下建立一个校验结果对象,对象中还创建一个获取错误信息的方法以便使用;并创建一个实现InitializingBean类的一个校验实现类,将hibernate validator 通过工厂校验的方式使其实例化,然后实现校验方法并返回校验结果对象。另外对字段有什么限制可在model层下对象模型的字段上添加注解如@NotBlank、@NotNull、@Max、@Min等。
跨域解决:在controller类上加上如下注解
1
2
3 1@CrossOrigin(allowCredentials = "true",allowedHeaders = "*") //实现跨域
2
3
DEFAULT_ALLOW_CREDENTIALS = true: 需配合前端设置,xhrFields授信后
使得跨域session共享 前端ajax请求添加设置:xhrFields:{withCredentials:true}
DEFAULT_ALLOWED_HEADERS = *:允许跨域传输所有的header参数,将用于使用token放入header域做session共享的跨域请求
项目要点
在mybatis-generator.xml配置文件中在对应生成表类名配置中加入;
前端 ajax 调用接口获取验证码 html/getotp.html,出现跨域请求问题 解决方法:@CrossOrigin(origins = {"*"}, allowCredentials = “true”) allowedHeaders 允许前端将 token 放入 header 做 session 共享的跨域请求。 allowCredentials 授信后,需前端也设置 xhfFields 授信才能实现跨域 session 共享。 xhrFields: {withCredentials: true},
统一前端返回格式CommonReturnType {status: xx ,object:xx} dataobject -> 与数据库对应的映射对象 model -> 用于业务逻辑service的领域模型对象 viewobject –> 用于前端交互的模型对象
使用 hibernate-validator 通过注解来完成模型参数校验;
insertSelective 中设置 keyProperty=“id” useGeneratedKeys=“true” 使得插入完后的 DO 生成自增 id 。 insertSelective与insert区别: insertSelective对应的sql语句加入了NULL校验,即只会插入数据不为null的字段值(null的字段依赖于数据库字段默认值)insert则会插入所有字段,会插入null。
数据库设计规范,设计时字段要设置为not null,并设置默认值,避免唯一索引在null情况下失效等类似场景
解决如果事务createorder下单如果回滚,该下单方法中获得流水号id回滚,使等到的id号可能再一次被使用 在generatorOrderNo方法前加注解: @Transactional(propagation = Propagation.REQUIRES_NEW)
使用聚合模型在itemModel加入PromoModel promoModel,若不为空表示其有未结束的秒杀活动;在orderModel中加入promoId,若不为空,则以秒杀方式下单;
项目中存在的问题
- 如何发现容量问题?
- 如何使系统得以水平扩展;
- 查询效率低下;
- 活动开始前页面被疯狂刷新;
- 库存行锁的问题;
- 下单操作多,缓慢
- 浪涌流量如何解决;
小结
对之前的SpringBoot电商秒杀项目,有了基础的模型和有技术的人员才能屹立不倒;