一:Service层接口设计

准备工作:新建三个包:service包、exception包、dto包,分别用来存放业务接口、自定义异常类、dto类。

1:定义接口

package org.myseckill.service;

import java.util.List;

import org.myseckill.dto.Exposer;
import org.myseckill.dto.SeckillExecution;
import org.myseckill.entity.Seckill;
import org.myseckill.exception.RepeatKillException;
import org.myseckill.exception.SeckillClosedException;
import org.myseckill.exception.SeckillException; public interface SeckillService { //查询所有
List<Seckill> getSeckillList();
//根据ID查询
Seckill getById(long seckillId);
//暴露秒杀网页地址
Exposer exportSeckillUrl(long seckillId); //md5用于与内部md5做比较,防止用于篡改url进行秒杀
SeckillExecution executeSeckill(long seckillId,long userPhone,String md5) throws SeckillException,RepeatKillException,SeckillClosedException; }

2:定义接口中用到的相关dto:

package org.myseckill.dto;

//封装暴露信息
public class Exposer {
//是否开启秒杀
private boolean exposed; private String md5; private long seckillId; private long now; private long start; private long end; public Exposer(boolean exposed, String md5, long seckillId) {
super();
this.exposed = exposed;
this.md5 = md5;
this.seckillId = seckillId;
} public Exposer(long now, long start, long end) {
super();
this.now = now;
this.start = start;
this.end = end;
} public Exposer(boolean exposed, long seckillId) {
super();
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;
} }
package org.myseckill.dto;

import org.myseckill.entity.SuccessKilled;

//封装秒杀后信息
public class SeckillExecution { private long seckillId;
private int state;
private String stateInfo;
private SuccessKilled successKilled;
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;
}
//秒杀成功的构造函数
public SeckillExecution(long seckillId, int state, String stateInfo,
SuccessKilled successKilled) {
super();
this.seckillId = seckillId;
this.state = state;
this.stateInfo = stateInfo;
this.successKilled = successKilled;
}
//秒杀失败的构造函数
public SeckillExecution(long seckillId, int state, String stateInfo) {
super();
this.seckillId = seckillId;
this.state = state;
this.stateInfo = stateInfo;
} }

3:定义接口中用到的自定义异常

package org.myseckill.exception;

//秒杀相关通用异常
public class SeckillException extends RuntimeException { public SeckillException() {
super();
// TODO Auto-generated constructor stub
} public SeckillException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
} public SeckillException(String message) {
super(message);
// TODO Auto-generated constructor stub
} public SeckillException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
} }
package org.myseckill.exception;

//重复秒杀异常
public class RepeatKillException extends SeckillException { public RepeatKillException(String message, Throwable cause) {
super(message, cause);
} public RepeatKillException(String message) {
super(message);
} public RepeatKillException(Throwable cause) {
super(cause);
} }
package org.myseckill.exception;

//秒杀已关闭异常
public class SeckillClosedException extends SeckillException { public SeckillClosedException() {
super(); } public SeckillClosedException(String message, Throwable cause) {
super(message, cause); } public SeckillClosedException(String message) {
super(message); } public SeckillClosedException(Throwable cause) {
super(cause); } }

二:Service层接口实现

1:在Service包下,新建一个Impl包,用于存放接口的实现类。

2:定义接口实现类SeckillServiceImpl

package org.myseckill.service.Impl;

import java.util.Date;
import java.util.List; import org.myseckill.dao.SeckillDao;
import org.myseckill.dao.SuccessKilledDao;
import org.myseckill.dto.Exposer;
import org.myseckill.dto.SeckillExecution;
import org.myseckill.entity.Seckill;
import org.myseckill.entity.SuccessKilled;
import org.myseckill.exception.RepeatKillException;
import org.myseckill.exception.SeckillClosedException;
import org.myseckill.exception.SeckillException;
import org.myseckill.service.SeckillService;
import org.myseckill.enums.SeckillStateEnum;
import org.myseckill.exception.RepeatKillException;
import org.myseckill.exception.SeckillClosedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils; public class SeckillServiceImpl implements SeckillService { //使用slf4j日志
private Logger logger=LoggerFactory.getLogger(this.getClass()); private SeckillDao seckillDao; private SuccessKilledDao successKilledDao; //用于加密的混淆字符串,随机串
private final String slat="asfdfadsf45qa@$E#iudkgj15=sdf=daf5"; //本例只用了4跳记录,实际项目中可以再定义一个selectall的SQL语句查询所有
public List<Seckill> getSeckillList() {
return seckillDao.queryAll(0, 4);
} @Override
public Seckill getById(long seckillId) { return seckillDao.queryById(seckillId);
} @Override
public Exposer exportSeckillUrl(long seckillId) {
Seckill seckill=seckillDao.queryById(seckillId);
if(seckill==null){//没有这个产品的秒杀记录,不进行暴露
return new Exposer(false, seckillId);
} Date now=new Date();
Date start=seckill.getStartTime();
Date end=seckill.getEndTime();
//若时间非法,不秒杀
if(now.getTime()<start.getTime() || now.getTime()>end.getTime()){
return new Exposer(false, seckillId, now.getTime(), start.getTime(), end.getTime());
}
//否则,进行秒杀网址暴露
String md5=getMD5(seckillId);
return new Exposer(true, md5, seckillId);
} //用md5加密
private String getMD5(long seckillId){
String base=seckillId+"/"+slat;
String md5=DigestUtils.md5DigestAsHex(base.getBytes());
return md5;
}
@Override
public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException, RepeatKillException, SeckillException {
if(md5==null||!md5.equals(getMD5(seckillId))){
throw new SeckillException("seckill data rewrite");
}
//执行秒杀逻辑:减库存+记录购买行为
try {
//记录购买行为
int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
if(insertCount <= 0 ){
//重复秒杀
throw new RepeatKillException("seckill repeated");
}else{
//减库存,热点商品竞争(高并发点)
int updateCount = seckillDao.reduceNumber(seckillId, new Date());
if(updateCount<=0){
//没有更新到记录,秒杀结束,rollback
throw new SeckillClosedException("seckill is closed");
}else{
//秒杀成功,commit
SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
return new SeckillExecution(seckillId,SeckillStateEnum.SUCCESS,successKilled);
}
} } catch(SeckillClosedException e1){
throw e1;
} catch(RepeatKillException e2){
throw e2;
}catch (Exception e) {
logger.error(e.getMessage(),e);
//所有异常转化为运行期异常
throw new SeckillException("seckill inner error:"+e.getMessage());
}
} }

3:把状态与状态信息常量,封装成枚举常量

在org.myseckill包下新建enums包,包中新建一个枚举类SeckillStateEnum:

package org.myseckill.enums;

//在实际开发中,全局常用的常量们用枚举常量存储
public enum SeckillStateEnum {
//定义一系列枚举常量
SUCCESS(1,"秒杀成功"),
END(0,"秒杀结束"),
REPEAT_KILL(-1,"重复秒杀"),
INNER_ERROR(-2,"系统异常"),
DATA_REWRITE(-3,"数据篡改"); private int state; private String stateInfo; SeckillStateEnum(int state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
} 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 static SeckillStateEnum stateOf(int index) {
//迭代枚举常量,返回state值等于index的常量
for (SeckillStateEnum stateEnum : values()) {
if (stateEnum.getState() == index) {
return stateEnum;
}
}
return null;
} }

4:修改SeckillExecution类的构造函数:

//秒杀成功的构造函数
public SeckillExecution(long seckillId, SeckillStateEnum success,SuccessKilled successKilled) {
super();
this.seckillId = seckillId;
this.state = success.getState();
this.stateInfo = success.getStateInfo();
this.successKilled = successKilled;
}
//秒杀失败的构造函数
public SeckillExecution(long seckillId, SeckillStateEnum success) {
super();
this.seckillId = seckillId;
this.state = success.getState();
this.stateInfo = success.getStateInfo();
}

三:Spring托管Service层的类

SpringIOC中,注入主要分为两种场景:

1:第三方类库提供的类,如:datasource等。使用XML中ref来注入;

2:自己定义的类,则在代码中,使用注解来注入;

1:在resource.spring包下,新建一个spring-service.xml,用于负责托管service层bean以及进行事务管理。

2:托管bean:

如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。

@Service用于标注业务层组件

@Controller用于标注控制层组件(如struts中的action)

@Repository用于标注数据访问组件,即DAO组件

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

component-scan标签默认情况下自动扫描指定路径下的包(含所有子包),将带有@Component、@Repository、@Service、@Controller标签的类自动注册到spring容器。

托管bean分两步:

首先在配置文件开启包扫描:

<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 包扫描,找到包下使用注解来依赖注入的属性,进行托管 -->
<context:component-scan base-package="org.myseckill.service"/> </beans>

然后,在bean类加注解:为类加组件注解,类中需要注入属性的加注入注解

@Service
public class SeckillServiceImpl implements SeckillService { @Autowired
private SeckillDao seckillDao;
@Autowired
private SuccessKilledDao successKilledDao; ......
}

3:声明式事务管理

事务管理主要有两种:

1:XML配置,使用tx:advice标签配置aop进行事务管理。好处是一次配置处处生效(可以自己配置切入点),坏处是,阅读代码时不知道哪个方法是被事务管理的,需要频繁查阅xml。

2:@Transactional注解:使用该注解注释的方法,表示把该方法交给spring进行事务管理。推荐使用这个方法,一是简便,二是在阅读代码时清晰地看到该方法被事务托管。

事务托管:

首先,在xml中配置事务管理器,并开启事务注解:

<?xml version="1.0" encoding="UTF-8"?>
<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-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<!-- 扫描service包下所有的类型 -->
<context:component-scan base-package="org.myseckill.service"></context:component-scan> <!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean> <!-- 配置基于注解的声明式事务
默认使用注解来管理事务行为-->
<tx:annotation-driven transaction-manager="transactionManager"/> </beans>

然后,在bean中,需要事务托管的方法前面,加@Transactional注解:

 @Transactional
public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException, RepeatKillExeception, SeckillException{}

SSM实战——秒杀系统之Service层接口设计与实现、Spring托管、声明式事务的更多相关文章

  1. SSM实战——秒杀系统之DAO层实体定义、接口设计、mybatis映射文件编写、整合Spring与Mybatis

    一:DAO实体编码 1:首先,在src目录下,新建org.myseckill.entity包,用于存放实体类: 2:实体类设计 根据前面创建的数据库表以及映射关系,创建实体类. 表一:秒杀商品表 对应 ...

  2. SSM实战——秒杀系统之Web层Restful url设计、SpringMVC整合、页面设计

    一:Spring整合SpringMVC 1:编写web.xml,配置DispatcherServlet <web-app xmlns="http://java.sun.com/xml/ ...

  3. SSM实战——秒杀系统前言

    项目来源:慕课网http://www.imooc.com/u/2145618/courses?sort=publish 项目开发流程:整合SSM框架——项目需求分析与实现——解决高并发优化 所用技术: ...

  4. SSM实战——秒杀系统之创建项目、管理依赖、设计数据库

    注:本项目使用Myeclipse开发. 一:项目创建 1:使用Myeclipse创建一个web project,命名为MySeckill,并转换为Maven项目. 2:创建项目文件目录如下: 上面四个 ...

  5. SSM实战——秒杀系统之高并发优化

    一:高并发点 高并发出现在秒杀详情页,主要可能出现高并发问题的地方有:秒杀地址暴露.执行秒杀操作. 二:静态资源访问(页面)优化——CDN CDN,内容分发网络.我们把静态的资源(html/css/j ...

  6. Spring Boot 揭秘与实战(二) 数据存储篇 - 声明式事务管理

    文章目录 1. 声明式事务 2. Spring Boot默认集成事务 3. 实战演练4. 源代码 3.1. 实体对象 3.2. DAO 相关 3.3. Service 相关 3.4. 测试,测试 本文 ...

  7. Java高并发秒杀API之Service层

    Java高并发秒杀API之Service层 第1章 秒杀业务接口设计与实现 1.1service层开发之前的说明 开始Service层的编码之前,我们首先需要进行Dao层编码之后的思考:在Dao层我们 ...

  8. 零基础学习java------39---------json格式交互,Restful(不懂),静态资源映射,SSM整合(ssm整合思想,application.xml文件详解(声明式事务管理),)

    一. json格式交互(知道) 1 . 回顾ajax基本语法 $.ajax({ url:"", // 请求的后台路径 data:{"":"" ...

  9. 02 整合IDEA+Maven+SSM框架的高并发的商品秒杀项目之Service层

    作者:nnngu 项目源代码:https://github.com/nnngu/nguSeckill 首先在编写Service层代码前,我们应该首先要知道这一层到底是干什么的. Service层主要负 ...

随机推荐

  1. 用 iOS-System-Services 框架获取iOS设备所用的设备信息

    参考资料地址 https://github.com/Shmoopi/iOS-System-Services 百度云盘下载地址 http://pan.baidu.com/s/1c05ot1m This ...

  2. 《Linux就是这个范儿》

    <Linux就是这个范儿> 基本信息 作者: 赵鑫磊    (加)Jie Zhang(张洁) 丛书名: 图灵原创 出版社:人民邮电出版社 ISBN:9787115359360 上架时间:2 ...

  3. 使用kubectl创建部署

    本文使用自己利用VirtubalBox搭建的集群环境,暂时只有一个Master.一个Node.如果想了解集群的搭建,可以参考我的文章离线环境安装Kubernetes集群以及使用kubeadm安装kub ...

  4. ThinkPHP错误信息处理

    index.php入口文件中打开APP_DEBUG// 开启调试模式define('APP_DEBUG', TRUE); // 开启Trace信息 'SHOW_PAGE_TRACE' =>tru ...

  5. Java垃圾回收机制(转)

    原文链接:Java垃圾回收机制 1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内 ...

  6. [转]Chart.js入门教程

    Chart.js是一个简单.面向对象.为设计者和开发者准备的图表绘制工具库. 相信大部分人都一样,看到一大筐用文本或者表格形式呈现的数据就头疼.因为这种呈现方式也太无聊了吧...而且这对于我们处理原始 ...

  7. RxJava 操作符 on和doOn 线程切换 调度 Schedulers 线程池 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  8. ajax hash缓存

    hash 模拟url路由 function hashdone(){        var hash;        hash=(!window.location.hash)?"#one&qu ...

  9. JQuery实现密码有短暂的显示过程和实现 input hint效果

    目录: 一.实现目的 二.问题思考 三.解决办法 1.输入用户名 2.输入密码短暂显示 一.实现目的 这几天做项目的时候,客户要求在文本框输入密码的时候,要求密码有短暂的显示过程,如下图: 二.问题思 ...

  10. 我对android davilk 虚拟机的理解

    Davilk虚拟机作为Android平台的一部分.Google公司花了大量时间思考针对低功耗手持设备的优化设计.在智能手机出现之前,与桌面设备相比,手持设备在内存和速度方面落后8-10年.它们的计算能 ...