在Dao层我们只完成了针对表的相关操作,包括写了接口方法和映射文件中的sql语句,并没有编写逻辑的代码,例如对多个Dao层方法的拼接,当我们用户成功秒杀商品时我们需要进行商品的减库存操作(调用SeckillDao接口)和增加用户明细(调用SuccessKilledDao接口),这些逻辑我们都需要在Service层完成。Dao层只进行数据的访问操作,接下来我们便进行Service层代码的编写。秒杀Service接口设计如下:
()创建service包用于存放我们的Service接口和其实现类。
()创建exception包用于存放service层出现的异常,例如重复秒杀商品异常、秒杀已关闭等异常。
()创建dto包作为传输层, 用于完成web和service层的数据传递。
()创建entity包用于业务数据的封装。
service包需要的相关类名和函数名如表6-10所示。

SeckillService.java:
public interface SeckillService {
List<Seckill> getSeckillList();
Seckill getById(long seckillId);
Exposer exportSeckillUrl(long seckillId);
SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException,RepeatKillException,SeckillCloseException;
SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5);
} SeckillServiceImpl.java:
public class SeckillServiceImpl implements SeckillService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SeckillDao seckillDao;
@Autowired
private SuccessKilledDao successKilledDao;
@Autowired
private RedisDao redisDao;
private final String salt = "sadkfjalsdjfalksj23423^&*^&%&!EBJKH#e™£4";
@Override
public List<Seckill> getSeckillList() {
return seckillDao.queryAll(, );
}
@Override
public Seckill getById(long seckillId) {
return seckillDao.queryById(seckillId);
}
@Override
public Exposer exportSeckillUrl(long seckillId) {
Seckill seckill = redisDao.getSeckill(seckillId);
if (seckill == null) {
seckill = seckillDao.queryById(seckillId);
if (seckill == null) {
return new Exposer(false, seckillId);
} else {
redisDao.putSeckill(seckill);
}
}
Date startTime = seckill.getStartTime();
Date endTime = seckill.getEndTime();
Date nowTime = new Date();
if (nowTime.getTime() < startTime.getTime()|| nowTime.getTime() >endTime.getTime()) {
return new Exposer(false, seckillId, nowTime.getTime(), startTime.getTime(),
endTime.getTime());
}
String md5 = getMD5(seckillId);
return new Exposer(true, md5, seckillId);
}
private String getMD5(long seckillId) {
String base = seckillId + "/" + salt;
String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
return md5;
}
@Override
@Transactional
public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException, RepeatKillException, SeckillCloseException {
if (md5 == null || !md5.equals(getMD5(seckillId))) {
throw new SeckillException("seckill data rewrite");
}
Date nowTime = new Date();
try {
int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
if (insertCount <= ) {
throw new RepeatKillException("seckill repeated");
} else {
int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
if (updateCount <= ) {
throw new SeckillCloseException("seckill is closed");
} else {
SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, successKilled);
}
}
} catch (SeckillCloseException e1) {
throw e1;
} catch (RepeatKillException e2) {
throw e2;
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new SeckillException("seckill inner error:" + e.getMessage());
}
}
@Override
public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5) {
if (md5 == null || !md5.equals(getMD5(seckillId))) {
return new SeckillExecution(seckillId, SeckillStatEnum.DATA_REWRITE);
}
Date killTime = new Date();
Map<String, Object> map = new HashMap<String, Object>();
map.put("seckillId", seckillId);
map.put("phone", userPhone);
map.put("killTime", killTime);
map.put("result", null);
try {
seckillDao.killByProcedure(map);
int result = MapUtils.getInteger(map, "result", -);
if (result == ) {
SuccessKilled sk = successKilledDao.
queryByIdWithSeckill(seckillId, userPhone);
return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, sk);
} else {
return new SeckillExecution(seckillId, SeckillStatEnum.stateOf(result));
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);
}
}
}
在dto包中创建Exposer.java,用于封装秒杀的地址信息。SeckillExecution.java,用于判断秒杀是否成功,成功就返回秒杀成功的所有信息(包括秒杀的商品id、秒杀成功状态、成功信息、用户明细),失败就抛出一个我们允许的异常(重复秒杀异常、秒杀结束异常)。
Exposer.java:
public class Exposer {
private boolean exposed;
private String md5;
private long seckillId;
private long now;
private long start;
private long end;
@Override
public String toString() {
return "Exposer{" +
"exposed=" + exposed +
", md5='" + md5 + '\'' +
", seckillId=" + seckillId +
", now=" + now +
", start=" + start +
", end=" + end +
'}';
}
public Exposer(boolean exposed, String md5, long seckillId) {
this.exposed = exposed;
this.md5 = md5;
this.seckillId = seckillId;
}
public Exposer(boolean exposed, long seckillId, long now, long start, long end) {
this.exposed = exposed;
this.seckillId = seckillId;
this.now = now;
this.start = start;
this.end = end;
}
public Exposer(boolean exposed, long seckillId) {
this.exposed = exposed;
this.seckillId = seckillId;
}
public boolean isExposed() {
return exposed;
}
public void setExposed(boolean exposed) {
this.exposed = exposed;
}
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public long getSeckillId() {
return seckillId;
}
public void setSeckillId(long seckillId) {
this.seckillId = seckillId;
}
public long getNow() {
return now;
}
public void setNow(long now) {
this.now = now;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public long getEnd() {
return end;
}
public void setEnd(long end) {
this.end = end;
}
}
SeckillExecution.java:
public class SeckillExecution {
private long seckillId;
private int state;
private String stateInfo;
private SuccessKilled successKilled;
@Override
public String toString() {
return "SeckillExecution{" +
"seckillId=" + seckillId +
", state=" + state +
", stateInfo='" + stateInfo + '\'' +
", successKilled=" + successKilled +
'}';
}
public SeckillExecution(long seckillId, SeckillStatEnum statEnum, SuccessKilled successKilled) {
this.seckillId = seckillId;
this.state = statEnum.getState();
this.stateInfo = statEnum.getStateInfo();
this.successKilled = successKilled;
}
public SeckillExecution(long seckillId, SeckillStatEnum statEnum) {
this.seckillId = seckillId;
this.state = statEnum.getState();
this.stateInfo = statEnum.getStateInfo();
}
public long getSeckillId() {
return seckillId;
}
public void setSeckillId(long seckillId) {
this.seckillId = seckillId;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public String getStateInfo() {
return stateInfo;
}
public void setStateInfo(String stateInfo) {
this.stateInfo = stateInfo;
}
public SuccessKilled getSuccessKilled() {
return successKilled;
}
public void setSuccessKilled(SuccessKilled successKilled) {
this.successKilled = successKilled;
}
}
然后需要在exception包下创建我们在秒杀业务过程中允许的异常,RepeatKillException.java用于处理重复的秒杀异常;SeckillCloseException.java用于处理秒杀关闭异常;SeckillException.java用于处理秒杀相关业务的异常。
RepeatKillException.java:
public class RepeatKillException extends SeckillException {
public RepeatKillException(String message) {
super(message);
}
public RepeatKillException(String message, Throwable cause) {
super(message, cause);
}
}
SeckillCloseException.java:
public class SeckillCloseException extends SeckillException {
public SeckillCloseException(String message) {
super(message);
}
public SeckillCloseException(String message, Throwable cause) {
super(message, cause);
}
}
SeckillException.java:
public class SeckillException extends RuntimeException {
public SeckillException(String message) {
super(message);
}
public SeckillException(String message, Throwable cause) {
super(message, cause);
}
}
在实现类SeckillServiceImpl.java中,我们用枚举的方式将异常函数中返回的常量进行封装,在enums包下创建一个枚举类型SeckillStatEnum.java,用于返回state和stateInfo这两个参数的相关数据。
SeckillStatEnum.java:
public enum SeckillStatEnum {
SUCCESS(,"秒杀成功"),
END(,"秒杀结束"),
REPEAT_KILL(-,"重复秒杀"),
INNER_ERROR(-,"系统异常"),
DATA_REWRITE(-,"数据篡改");
private int state;
private String stateInfo;
SeckillStatEnum(int state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
}
public int getState() {
return state;
}
public String getStateInfo() {
return stateInfo;
}
public static SeckillStatEnum stateOf(int index) {
for (SeckillStatEnum state : values()) {
if (state.getState() == index) {
return state;
}
}
return null;
}
}
使用Spring托管Service依赖配置:
在spring包下创建一个spring-service.xml文件,然后采用注解的方式将Service的实现类加入到Spring IOC容器中,然后在Service实现类的方法中,在需要进行事务声明的方法上加上事务的注解,配置如下。
spring-service.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="org.seckill.service"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

秒杀系统-service的更多相关文章

  1. 高并发秒杀系统--Service接口设计与实现

    [DAO编写之后的总结] DAO层    -->    接口设计 + SQL编写 DAO拼接等逻辑    -->    统一在Service层完成 [Service层的接口设计] 1.接口 ...

  2. 高并发秒杀系统--Service事务管理与继承测试

    [Spring IoC的类型及应用场景]  [Spring事务使用方式] [Spring事务的特性] [Spring事务回滚的理解] [Service声明式事务的配置] 1.配置事务管理器 2.配置基 ...

  3. Java高并发秒杀系统API之SSM框架集成swagger与AdminLTE

    初衷与整理描述 Java高并发秒杀系统API是来源于网上教程的一个Java项目,也是我接触Java的第一个项目.本来是一枚c#码农,公司计划部分业务转java,于是我利用业务时间自学Java才有了本文 ...

  4. Java高并发秒杀系统【观后总结】

    项目简介 在慕课网上发现了一个JavaWeb项目,内容讲的是高并发秒杀,觉得挺有意思的,就进去学习了一番. 记录在该项目中学到了什么玩意.. 该项目源码对应的gitHub地址(由观看其视频的人编写,并 ...

  5. SSM实现秒杀系统案例

    ---------------------------------------------------------------------------------------------[版权申明:本 ...

  6. SpringBoot开发案例从0到1构建分布式秒杀系统

    前言 ​最近,被推送了不少秒杀架构的文章,忙里偷闲自己也总结了一下互联网平台秒杀架构设计,当然也借鉴了不少同学的思路.俗话说,脱离案例讲架构都是耍流氓,最终使用SpringBoot模拟实现了部分秒杀场 ...

  7. 从构建分布式秒杀系统聊聊Lock锁使用中的坑

    前言 在单体架构的秒杀活动中,为了减轻DB层的压力,这里我们采用了Lock锁来实现秒杀用户排队抢购.然而很不幸的是尽管使用了锁,但是测试过程中仍然会超卖,执行了N多次发现依然有问题.输出一下代码吧,可 ...

  8. 基于SpringMVC+Spring+MyBatis实现秒杀系统【概况】

    前言 本教程使用SpringMVC+Spring+MyBatis+MySQL实现一个秒杀系统.教程素材来自慕课网视频教程[https://www.imooc.com/learn/631].有感兴趣的可 ...

  9. springboot项目:Redis分布式锁的使用(模拟秒杀系统)

    模拟秒杀系统: 第一步:编写Service package com.payease.service; /** * liuxiaoming * 2017-12-14 */ public interfac ...

随机推荐

  1. JAVA获取运行程序的src路径

    JavaPrj使用: System.getProperty("user.dir")+"\\src"或直接"\\src"JavaWeb使用Th ...

  2. J2EE十三个技术规范

    从事Java开发的童鞋都知道,java是一种非常棒的语言,能够实现跨平台运行.它屏蔽了具体的平台环境的要求,也就是说,无论是windows,还是Unix.Linux系统,只要支持Java虚拟机,就可以 ...

  3. jsp jstl quote symbol expected

    org.apache.jasper.JasperException: /WEB-INF/jsp/user/index.jsp (line: 2, column: 27) quote symbol ex ...

  4. uni-app开发小程序准备阶段

    1.软件安装 开始之前,开发者需先下载安装如下工具: HBuilderX:官方IDE下载地址 下面开发工具根据需求进行安装: 微信小程序开发工具安装 https://developers.weixin ...

  5. leetcode1029

    class Solution(object): def twoCitySchedCost(self, costs: 'List[List[int]]') -> int: costs = sort ...

  6. Django02-路由系统urls

    一.路由配置系统(URLconf) 分为:静态路由动态路由 1.URL配置 URL配置(URLconf)就像Django所支撑网站的目录.它的本质是URL与该URL调用的视图函数之间的映射表 语法: ...

  7. @ResponseBody 与 response.getWriter.write

    @responseBody注解的使用 1. @responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通 ...

  8. java学习-- equals和hashCode的关系

    hashcode的目的就是在hashset或者hashmap等中比较两个对象相等时,减少equals的使用次数来提高效率 以下为摘录 java中hashcode和equals的区别和联系 HashSe ...

  9. jquery之过滤filter,not

    <body> <h1>欢迎来到我的主页</h1> <p>我是唐老鸭</p> <p class="intro"> ...

  10. 编译wiredtiger rpm包

    1.安装rpm-build 使用rpmbuild打包rpm包前,首先安装rpmbuild: yum install rpm-build -y 2.创建打包文件目录 mkdir  -p  /root/r ...