Spring + SpringMVC + MyBatis 集成(二)

前言

继续进行整合

Service 层的配置

spring-service 配置

1
/resources/spring/spring-service.xml

在上述文件中配置 Service 层。
主要有三步:

  1. 扫描 service 包下所有使用注解的类型
  2. 配置事务管理器,并注入数据库的连接池
  3. 配置基于注解的声明式事务,默认使用注解来管理事务行为

采用注解来管理事务行为,自由选择事务开启,提高性能,减少开启事务开销。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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.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 类代码

在 Service 包里面建立 Service 类,在类级别使用 @Service 注解。DAO 的类则使用 @Autowired 类,需要开启事务的方法则 使用 @Transactional 注解。

比如下:

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
package org.seckill.service.impl;

import 此处省略

/**
* Created by ARNO on 2016/6/6/006.
*/

@Service
public class SeckillServiceImpl implements SeckillService {

private Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
private SeckillDao seckillDao;

@Autowired
private SuccessKilledDao successKilledDao;

@Autowired
private RedisDao redisDao;

// MD5 字符串,用于混淆代码
private final String slat = "DFG$%^$%^YH45r7dfgdfgdfY#$%GBERH&^*OL>:";

/**
* 查询所有秒杀记录
*
* @return
*/

@Override
public List<Seckill> getSeckillList() {
return seckillDao.queryAll(0, 4);
}

/**
* 查询单个秒杀记录
*
* @param seckillId
* @return
*/

@Override
public Seckill getById(long seckillId) {
return seckillDao.queryById(seckillId);
}

/**
* 执行秒杀操作
*
* @param seckillId
* @param userPhone
* @param md5
*
* 使用注解控制事务方法的优点:
* 1、开发团队达成一致约定,明确标注事务方法的编程风格
* 2、保证事务方法的执行时间尽可能短,不要穿插其他网络操作(RPC/HTTP请求)。可以剥离到事务方法外部。
* 3、不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制。
*/

@Override
@Transactional
public SeckillExecution executeSekill(long seckillId, long userPhone, String md5)
throws SeckillException, SeckillCloseException, RepeatKillException {

此处省略
}
}

Service 类接口

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
package org.seckill.service;

import 此处省略

/**
* Created by ARNO on 2016/6/3/003.
* 业务接口:站在“使用者”角度设计接口
* 三个方面:
* 1. 方法定义粒度(明确,比如执行描述)
* 2. 参数 (越简练越好,少用 map 这样类型)
* 3. 返回类型 (友好,不建议用 map 这种类型。可以抛出异常)
*/

public interface SeckillService {
/**
* 查询所有秒杀记录
*
* @return
*/

List<Seckill> getSeckillList();

/**
* 查询单个秒杀记录
*
* @param seckillId
* @return
*/

Seckill getById(long seckillId);

/**
* 输出秒杀接口地址。
* 秒杀开启时输出秒杀接口地址,否则输出系统时间和秒杀时间
* @param seckillId
*/

Exposer exportSeckillUrl(long seckillId);

/**
* 执行秒杀操作
* @param seckillId
* @param userPhone
* @param md5
*/

SeckillExecution executeSekill(long seckillId, long userPhone, String md5) throws SeckillException,SeckillCloseException,RepeatKillException;
}

整合Spring MVC

spring-web.xml 配置

1
/resources/spring/spring-web.xml

在上述文件中配置 web 层。
主要有四步:

  1. 开启 SpringMVC 注解模式
  2. 静态资源默认 servlet 配置
  3. 配置 jsp 显示 ViewResolver
  4. 扫描 Web 相关的 bean
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
<?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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">


<!-- No bean named 'cacheManager' is defined 是因为IDEA 自动补全导致的错误。 后面配置成了 cache -->

<!--配置 SpringMVC-->
<!--1、开启 SpringMVC 注解模式-->
<!--简化配置:
1.1 自动注册 DefaultAnnotationHandlerMapping(使用注解驱动的 Handler 映射),AnnotationMethodHandlerAdapter(注解方法的 Handler 适配器)
1.2 提供一系列:数据绑定,数字和日期的 format @NumberFormat,@DataTimeFormat,xml,json 默认读写支持
-->

<mvc:annotation-driven/>


<!--2、静态资源默认 servlet 配置-->
<!--2.1 加入对静态资源的处理:js,gif,png
2.2 允许使用“/”做整体映射
-->

<mvc:default-servlet-handler/>

<!--3、配置 jsp 显示 ViewResolver-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!--4、扫描 Web 相关的 bean-->
<context:component-scan base-package="org.seckill.web" />
</beans>

此处有一个坑,Windows 下使用 IntelliJ IDEA 2016.1.2(64) 版本,把 xmlns:mvc 配置成 cache,导致报错,No bean named ‘cacheManager’ is defined。

web.xml 配置

1
/WEB-INF/web.xml

在上述文件中配置web.xml

代码如下:

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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">

<!--修改 servlet 版本为 3.1 -->

<!--配置 DispatcherServlet-->
<servlet>
<servlet-name>seckill-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置 SpringMVC 需要加载的配置的文件:spring-dao.xml,spring-service.xml,spring-web.xml
MyBatis -> Srping -> SpringMVC
-->

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
</servlet>


<servlet-mapping>
<servlet-name>seckill-dispatcher</servlet-name>
<!--默认匹配所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

Controller 类代码

在类界别使用 @Controller 注解,@RequestMapping 注解绑定 URL 路径。

代码范例如下:

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
package org.seckill.web;

import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillExecution;
import org.seckill.dto.SeckillResult;
import org.seckill.entity.Seckill;
import org.seckill.enums.SeckillStatEnum;
import org.seckill.exception.RepeatKillException;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.List;

/**
* Created by ARNO on 2016/6/6/006.
*/


@Controller
@RequestMapping("/seckill") // url:/模块/资源/{id}/细分/ seckill/list
public class SeckillController {

private Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
private SeckillService seckillService;

@RequestMapping(value = "list", method = RequestMethod.GET)
public String list(Model model) {
// 获取列表页
List<Seckill> list = seckillService.getSeckillList();
model.addAttribute("list", list);
// list.jsp + modle =ModelAndView
return "list";
}

@RequestMapping(value = "/{seckillId}/detail", method = RequestMethod.GET)
public String detail(@PathVariable("seckillId") Long seckillId, Model model) {
if (seckillId == null) {
return "redirct:/seckill/list";
}
Seckill seckill = seckillService.getById(seckillId);
if (seckillId == null) {
return "forward:/seckill/list";
}
model.addAttribute("seckill", seckill);
return "detail";
}
此处省略
}

友好的 Restful 设计范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
URL 设计
/模块/资源/{标识}/集合1/...


GET /seckill/list
友好

POST /seckill/execute/{seckillId}
不友好

POST /seckill/{seckillId}/execution
友好
(execution,名词:执行)

GET /seckill/delete/{id}
不友好

DELETE /seckill/{id}/delete
友好

参考资料