一篇SSM框架整合友好的文章(二)
上一篇讲述了DAO 层,mybatis实现数据库的连接,DAO层接口设计,以及mybtis和spring的整合。DAO层采用接口设计方式实现,接口和SQL实现的分离,方便维护。DAO层所负责的仅仅是接口的设计和实现,而负责的逻辑即一个或多个DAO层接口的拼接是在Sevice层中完成。这篇文章接上篇文章,主要讲述Service层的实现、和Spring的整合以及声明如何声明事物。
###一、Service层接口设计
业务接口设计应当站在“使用者”角度设计接口,应遵循三个规范:合理的命令,明确的参数,返回结果(正常接口/异常结果)。本例子采用的Java高并发的秒杀API系列课程的例子,创建设计的业务逻辑接口如下:
public interface SeckillService {
/**
* 查询所有秒杀记录
* @return
*/
List<Seckill> getSerkillList();
/**
* 查询单个秒杀记录
* @param seckillId
* @return
*/
Seckill getById(long seckillId);
/**
* 秒杀开启时输出秒杀接口地址,
* 否则输出系统时间和秒杀时间
* @param seckillId
*/
Exposer exportSeckillUrl(long seckillId);
/**
*执行秒杀接口
*/
SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException,RepeatKillException,SeckillCloseException;
二、Service接口的实现
直接上代码了,在这里讲下秒杀业务的逻辑:首先是获取秒杀列表,点击列表进入秒杀详情页,这时获取系统时间,如果秒杀开始,获取秒杀地址,点击秒杀,执行秒杀。所以业务逻辑也只设计了这相关的4个业务逻辑。其中使用了dto层去传递响应数据,以及自定义异常,所有的异常都继承运行异常,这是为了方便spring自动回滚,这两个知识点,自行看源码。
package org.forezp.service.impl;
@Service
public class SeckillServiceImpl implements SeckillService{
private Logger logger= LoggerFactory.getLogger(this.getClass());
//注入service依赖
@Autowired
private SeckillDao seckillDao;
@Autowired
private SuccessKilledDao successKilledDao;
//MD5盐值字符串,用户混淆MD5
private final String slat="sfsa=32q4r23234215ERWERT^**%^SDF";
public List<Seckill> getSerkillList() {
return seckillDao.queryAll(0,4);
}
public Seckill getById(long seckillId) {
return seckillDao.queryById(seckillId);
}
public Exposer exportSeckillUrl(long seckillId) {
Seckill seckill =seckillDao.queryById(seckillId);
if(seckill==null){
return new Exposer(false,seckillId);
}
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+"/"+slat;
String md5= DigestUtils.md5DigestAsHex(base.getBytes());
return md5;
}
@Transactional
/**
*使用注解控制事务方法的优点
* 1:开发团队达成一致约定,明确标注事务方法的编程风格
* 2:保证事务方法的执行时间尽可能短,不要穿插其他网络请求,RPC/HTTP请求或者剥离到事务方法外
* 3:不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制
*/
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);
//唯一:seckillId,userphone
if(insertCount<=0){
//重复秒杀
throw new RepeatKillException("seckill repeated");
}else{
//减库存,热点商品竞争
int updateCount=seckillDao.reduceNumber(seckillId,nowTime);
if(updateCount<=0){
//没有更新到记录,秒杀结束 rollback
throw new SeckillCloseException("seckill is closed");
}else{
//秒杀成功 commit
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);
//所有的编译期异常,转化为运行期异常(运行时异常,spring可以做rollback操作)
throw new SeckillException("seckill inner error:"+e.getMessage());
}
}
//抛出异常是为了告诉spring是否rollback,此处使用存储过程的话,就不需要抛异常了
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);
//执行存储过程,result被赋值
try {
seckillDao.killByProcedure(map);
int result=(Integer) map.get("result");
if(result==1){
SuccessKilled successKilled=successKilledDao.queryByIdWithSeckill(seckillId,userPhone);
return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS,successKilled);
}else{
return new SeckillExecution(seckillId,SeckillStatEnum.stateof(result));
}
} catch (Exception e) {
logger.error(e.getMessage(),e);
return new SeckillExecution(seckillId,SeckillStatEnum.INNER_ERROE);
}
}
}
三、Sping托管 service的实现类
和上一篇文章使用spring托管dao接口一样,这里也需要用 spring 托管service. spring ioc 使用对象工程模式,对所有的注入的依赖进行了管理,暴露出了一致性的访问接口,当我们需要某个对象时,直接从spring ioc中取就行了,不需要new,也不需要对它们的生命周期进行管理。更为重要的是spring 自动组装依赖,比如最终的接口controller依赖service,而service依赖dao,dao依赖sessionfactory,而sessionfactory依赖datasource,这些层层依赖是通过spring管理并层层组装,只要我们简单配置和注解就可以方便的使用,代码的分层和编程的艺术在spring框架中展现得淋漓尽至。
本项目采用spring ioc :
1.xml配置
2.包扫描
3.annotation注解。
创建sping-service.xml
采用包扫描+注解方式,首先在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.xsd http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
>
<!--扫描service包下所有使用注解的类型-->
<context:component-scan base-package="org.forezp.service"/>
然后在org,forezp.service包下的类采用注解。比如@Service 注解声明是一个service, @Autowired注入service 所需依赖。
@Service//声明是一个service
public class SeckillServiceImpl implements SeckillService{
//注入service依赖
@Autowired
private SeckillDao seckillDao;
@Autowired
private SuccessKilledDao successKilledDao;
}
只需要一个包扫描和几个简单的注解就可以将service注解到spring ioc容器中。
四、spring声明式事物
在秒杀案例中,我们需要采用事物来防止数据的正确性,防止重复秒杀,防止库存不足、库存剩余等情况。一般使用事物需要开启事物/经常一些列的操作,提交或者回滚。spring声明式事物,就是将事物的开启、提交等托管给spring管理,我们只需要注重如何修改数据。
配置spring 声明式事物
在spring-service.xml中配置:
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置基于注解的声明式事务
默认使用注解来管理事务行为
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在需要事物的业务逻辑下加 @Transactional注解。
比如在开启秒杀方法:
@Transactional
public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException {
if(md5==null ||!md5.equals(getMD5(seckillId))){
}
注意:
1开发团队达成一致约定,明确标注事务方法的编程风格
2:保证事务方法的执行时间尽可能短,不要穿插其他网络请求,RPC/HTTP请求或者剥离到事务方法外
3:不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制
五、单元测试
需要配置:
@ContextConfiguration({
“classpath:spring/spring-dao.xml”,
“classpath:spring/spring-service.xml”
})
直接上代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({
"classpath:spring/spring-dao.xml",
"classpath:spring/spring-service.xml"
})
public class SeckillServiceTest {
private final Logger logger=LoggerFactory.getLogger(this.getClass());
@Autowired
private SeckillService seckillService;
@Test
public void getSerkillList() throws Exception {
List<Seckill> list=seckillService.getSerkillList();
System.out.println(list);
//执行结果[Seckill{seckillId=1000, name='1000元秒杀iphone6'..... 省略。。。]
}
@Test
public void getById() throws Exception {
long id=1000;
Seckill seckill=seckillService.getById(id);
System.out.println(seckill);
//执行结果:Seckill{seckillId=1000, name='1000元秒杀iphone6', number=100, startTime=Sun Nov 01 00:00:00 CST 2015,。。。。}
}
@Test
public void exportSeckillUrl() throws Exception {
long id=1000;
Exposer exposer=seckillService.exportSeckillUrl(id);
System.out.println(exposer);
}
@Test
public void executeSeckill() throws Exception {
long id=1000;
long phone=13502171122L;
String md5="e83eef2cc6b033ca0848878afc20e80d";
SeckillExecution execution=seckillService.executeSeckill(id,phone,md5);
System.out.println(execution);
}
}
这篇文章主要讲了service业务接口的编写和实现,以及采用xml和注解方式讲service 注入到spring ioc,以及声明式事物,不得不感叹spring 的强大。下一篇文章讲讲述 web层的开发,spring mvc的相关配置。感谢大家,再接再厉,晚安。_。
扫码关注公众号有惊喜
(转载本站文章请注明作者和出处 方志朋的博客)
一篇SSM框架整合友好的文章(二)的更多相关文章
- 一篇SSM框架整合友好的文章(一)
转载请标明出处: http://blog.csdn.net/forezp/article/details/53730333 本文出自方志朋的博客 最近实在太忙,之前写的<rxjava系列文章&g ...
- 一篇SSM框架整合友好的文章(三)
###一.SpringMVC理论 它始终是围绕 handler. 数据模型 model. 页面view进行开发的. 运行流程图: 通过mvc配置文件,配置"中央处理器"dispat ...
- 接入上一篇SSM框架整合示例
需要创建相应的包与文件夹 Book数据表作为本示例数据 搭建项目开始 首先有bean后 private int id; private String name; private int cnt; 第一 ...
- SSM框架整合练习——一个简单的文章管理系统
使用SSM框架搭建的简易文章管理系统,实现了简单的增删改查功能. @ 目录 开发工具版本: 最终的项目结构 IDEA+Maven搭建项目骨架 1. 新建Maven项目: 2. 在新建的项目中添加所需要 ...
- SpringMVC--从理解SpringMVC执行流程到SSM框架整合
前言 SpringMVC框架是SSM框架中继Spring另一个重要的框架,那么什么是SpringMVC,如何用SpringMVC来整合SSM框架呢?下面让我们详细的了解一下. 注:在学习SpringM ...
- SSM框架整合环境构建——基于Spring4和Mybatis3
目录 环境 配置说明 所需jar包 配置db.properties 配置log4j.properties 配置spring.xml 配置mybatis-spring.xml 配置springmvc.x ...
- springmvc(二) ssm框架整合的各种配置
ssm:springmvc.spring.mybatis这三个框架的整合,有耐心一步步走. --WH 一.SSM框架整合 1.1.整合思路 从底层整合起,也就是先整合mybatis与spring,然后 ...
- ssm框架整合-过程总结(第二次周总结)
距离上次写博客已经有4.5天的时间了. 这次写博客目的是总结一下项目开始到现在,过程中遇到的问题.和学到的知识.经验. 初略总结下自己从中学到的: Spring :在学习中被反复强调的Ioc(反转控制 ...
- SSM框架整合项目 :租房管理系统
使用ssm框架整合,oracle数据库 框架: Spring SpringMVC MyBatis 导包: 1, spring 2, MyBatis 3, mybatis-spring 4, fastj ...
随机推荐
- 性能测试工具Jmeter11-Jmeter图形监控扩展
插件下载地址:http://jmeter-plugins.org/downloads/all/ 1.首先将JmeterPluging.jar包复制到Jmeter的Lib目录下面的ext目录下面,然后重 ...
- [转]javascript实现限制上传文件的大小
本文转自:http://www.micmiu.com/lang/javascript/js-check-filesize/ 目录 基本思路 示例 [一].基本思路 在FireFox.Chrome浏览器 ...
- winscp介绍与使用
winscp介绍 WinSCP 是一个 Windows 环境下使用的 SSH 的开源图形化 SFTP 客户端.同时支持 SCP 协议.它的主要功能是在本地与远程计算机间安全地复制文件,并且可以直接编辑 ...
- 明码(C++)
2.明码(结果填空) (满分7分) 注意事项:问题的描述在考生文件夹下对应题号的“题目.txt”中.相关的参考文件在同一目录中.请先阅读题目,不限解决问题的方式,只要求提交结果.必须通过浏览器提交答案 ...
- spring boot基本认识
大家眼中的spring boot:https://www.zhihu.com/question/39483566-------------------------------------------- ...
- DB2去重复的几种方法
DB2去重的几种方法 有两个意义上的重复记录,一是完全重复的记录,也即所有字段均重复的记录,二是部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略. 例如下表:tabl ...
- js超链接
_blank -- 在新窗口中打开链接 _parent -- 在父窗体中打开链接 _self -- 在当前窗体打开链接,此为默认值 _top -- 在当前窗体打开链接,并替换当前的整个窗体(框架页) ...
- .net程序集标示与绑定上下文
之前在实现Autofac扫描自加载程序集实现IOC时候遇到程序集依赖的问题,在网上搜了一下,没有发现中文世界的相关描述.随取google拿了几篇文章,翻译&自己的理解,之后会写一些小demo, ...
- HttpClient4.3.3 禁止自动重定向
HttpClient4.3中默认允许自动重定向,导致程序中不能跟踪跳转情况,其实只需要在RequestConfig中setRedirectsEnabled(false)即可(默认是true): pri ...
- poj 3140 树形去边差异最小
http://poj.org/problem?id=3140 依然是差异最小问题,不过这次是去边.思路是这样的,先记录每个点的子节点个数,然后遍历每个边. 有两个问题要注意: abs可能会出编译适配问 ...