一篇SSM框架整合友好的文章(二)
2017-04-18 17:48
411 查看
上一篇讲述了DAO 层,mybatis实现数据库的连接,DAO层接口设计,以及mybtis和spring的整合。DAO层采用接口设计方式实现,接口和SQL实现的分离,方便维护。DAO层所负责的仅仅是接口的设计和实现,而负责的逻辑即一个或多个DAO层接口的拼接是在Sevice层中完成。这篇文章接上篇文章,主要讲述Service层的实现、和Spring的整合以及声明如何声明事物。
业务接口设计应当站在“使用者”角度设计接口,应遵循三个规范:合理的命令,明确的参数,返回结果(正常接口/异常结果)。本例子采用的Java高并发的秒杀API系列课程的例子,创建设计的业务逻辑接口如下:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
直接上代码了,在这里讲下秒杀业务的逻辑:首先是获取秒杀列表,点击列表进入秒杀详情页,这时获取系统时间,如果秒杀开始,获取秒杀地址,点击秒杀,执行秒杀。所以业务逻辑也只设计了这相关的4个业务逻辑。其中使用了dto层去传递响应数据,以及自定义异常,所有的异常都继承运行异常,这是为了方便spring自动回滚,这两个知识点,自行看源码。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
和上一篇文章使用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中声明包扫描:
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
然后在org,forezp.service包下的类采用注解。比如@Service 注解声明是一个service, @Autowired注入service 所需依赖。
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
只需要一个包扫描和几个简单的注解就可以将service注解到spring ioc容器中。
在秒杀案例中,我们需要采用事物来防止数据的正确性,防止重复秒杀,防止库存不足、库存剩余等情况。一般使用事物需要开启事物/经常一些列的操作,提交或者回滚。spring声明式事物,就是将事物的开启、提交等托管给spring管理,我们只需要注重如何修改数据。
配置spring 声明式事物
在spring-service.xml中配置:
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
在需要事物的业务逻辑下加 @Transactional注解。
比如在开启秒杀方法:
2
3
4
5
1
2
3
4
5
注意:
1开发团队达成一致约定,明确标注事务方法的编程风格
2:保证事务方法的执行时间尽可能短,不要穿插其他网络请求,RPC/HTTP请求或者剥离到事务方法外
3:不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制
需要配置:
@ContextConfiguration({
“classpath:spring/spring-dao.xml”,
“classpath:spring/spring-service.xml”
})
直接上代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
这篇文章主要讲了service业务接口的编写和实现,以及采用xml和注解方式讲service 注入到spring ioc,以及声明式事物,不得不感叹spring 的强大。下一篇文章讲讲述 web层的开发,spring mvc的相关配置。感谢大家,再接再厉,晚安。^_^。
一、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;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
二、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); 161fc 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); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
三、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"/>1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
然后在org,forezp.service包下的类采用注解。比如@Service 注解声明是一个service, @Autowired注入service 所需依赖。
@Service//声明是一个service public class SeckillServiceImpl implements SeckillService{ //注入service依赖 @Autowired private SeckillDao seckillDao; @Autowired private SuccessKilledDao successKilledDao; }1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
只需要一个包扫描和几个简单的注解就可以将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"/>1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
在需要事物的业务逻辑下加 @Transactional注解。
比如在开启秒杀方法:
@Transactional public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException { if(md5==null ||!md5.equals(getMD5(seckillId))){ }1
2
3
4
5
1
2
3
4
5
注意:
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); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
这篇文章主要讲了service业务接口的编写和实现,以及采用xml和注解方式讲service 注入到spring ioc,以及声明式事物,不得不感叹spring 的强大。下一篇文章讲讲述 web层的开发,spring mvc的相关配置。感谢大家,再接再厉,晚安。^_^。
相关文章推荐
- 一篇SSM框架整合友好的文章(三)
- 一篇SSM框架整合友好的文章(三)
- 一篇SSM框架整合友好的文章(一)
- 一篇SSM框架整合友好的文章(三)
- 一篇SSM框架整合友好的文章(一)
- 一篇SSM框架整合友好的文章(二)
- 一篇SSM框架整合友好的文章(一)
- SSM框架整合的文章(1)
- 一篇SSM框架整合友好的文章(二)
- SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)不错的一片文章
- SSM框架整合的文章(2)
- 文章标题 SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
- 文章标题 SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
- SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)很好的文章收藏
- SSM框架整合( Spring 、 SpringMVC 和 Mybatis )
- maven + SSM框架整合( Spring 、 SpringMVC 和 Mybatis )
- SSM框架整合
- spring+websocket整合(springMVC+spring+MyBatis即SSM框架和websocket技术的整合)
- java基于ssm框架整合的多数据源配置
- springmvc+mybatis+spring 整合 bootstrap html5 mysql oracle SSM框架源码 SSH maven