1. 前言
上一篇教程中我們測試了本地開發環境,在本節教程中,我們來配置應用程序接口的開發環境。在開始配置環境之前先來了解一下什么是應用程序接口。
我們知道類似于C/S架構的程序本地是沒有數據的,如果想要展示數據必須從遠程服務器獲取。那么客戶端和服務器如何通信呢?最典型的一個例子就是在你在使用瀏覽器的時候,例如在你想要訪問簡書首頁的時候只需要在瀏覽器中地址欄中輸入簡書的首頁地址即可。那么在這個過程中究竟發生了什么事情呢?簡單的說,就是瀏覽器向服務器發送一條請求,服務器在收到瀏覽器的請求之后判斷瀏覽器需要什么,然后就給瀏覽器返回什么,瀏覽器接收到結果之后就會對結果進行解析,最后就是你看到的簡書首頁了。這里面有啥東西呢?首先是請求,Request,就是你輸入的那條url;還有一個結果,Response,就是服務器返回給瀏覽器的內容;最后瀏覽器解析Response,以一種恰當的形式顯示給用戶。那么我們可以簡單的把應用程序接口理解成是分析處理url的一段程序。
2. RESTful API
關于RESTful API我不想講太多,關于她的探討和說明的文章有很多,這里推薦這篇文章理解RESTful架構。
3. SpringMVC+Mybatis環境配置
SpringMVC+Mybatis的環境配置請參考之前寫的那篇文章。
SpringMVC+Mybatis本地開發環境搭建示例(三)
4. 配置Swagger2
在配置之前先說明一下,Swagger2是啥?這是一個幫你自動生成應用程序接口說明文檔的第三方庫。有了這個高級的第三方框架我們就不需要自己寫說明文檔啦,光是想想就好開心的呢。
4.1 引入第三方庫
在pom.xml文件中引入以下依賴:
<!-- 8.Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.5.0</version>
</dependency>
4.2 編寫Swagger2配置類SwaggerConfig.java
SwaggerConfig.java 文件在包cn.semiwarm.api.swagger
中
package cn.semiwarm.api.swagger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger配置文件
* Created by alibct on 2017/2/28.
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createUserInfo() {
Docket docket = new Docket(DocumentationType.SWAGGER_2);
Contact contact = new Contact("半暖商城", "www.semiwarm.cn", "semiwarm@163.com");
ApiInfo apiInfo = new ApiInfo("API文檔說明", "SemiWarm API文檔", "V1.0", "", contact, "", "");
docket.apiInfo(apiInfo);
return docket;
}
}
4.3 在spring-mvc.xml
文件中添加以下配置
<!-- 3.配置swagger2 -->
<bean name="swaggerConfig" class="cn.semiwarm.api.swagger.SwaggerConfig"/>
4.4 編寫測試接口
經過上面的配置,此時運行項目,在瀏覽器中輸入http://localhost:8080/swagger-ui.html
就能夠訪問Swagger2的首頁了。也就API說明文檔首頁。
下面開始編寫測試接口。
4.4.1 編寫User.java
包cn.semiwarm.api.entity
package cn.semiwarm.api.entity;
import java.io.Serializable;
import java.util.Date;
/**
* 描述:用戶實體類
* <p>
* Created by alibct on 2017/2/23.
*/
public class User implements Serializable {
private Long userId; // 用戶ID
private String userName; // 用戶名稱
private String userPhone; // 用戶手機號
private String password; // 密碼
private Boolean status; // 賬號是否可用->默認可用
private Date createAt; // 用戶創建時間
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public Date getCreateAt() {
return createAt;
}
public void setCreateAt(Date createAt) {
this.createAt = createAt;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userPhone='" + userPhone + '\'' +
", password='" + password + '\'' +
", status=" + status +
", createAt=" + createAt +
'}';
}
}
4.4.2 編寫UserMapper.java
包cn.semiwarm.api.mapper
package cn.semiwarm.api.mapper;
import cn.semiwarm.api.entity.User;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
/**
* 描述:User表的基本查詢接口
*
* Created by alibct on 2017/2/23.
*/
public interface UserMapper extends BaseMapper<User> {
int add(User user);
int delete(User user);
int update(User user);
User findById(Serializable id);
List<User> findAll();
// 根據用戶手機號查詢用戶
User findUserByPhone(Serializable userPhone);
// 根據用戶名稱查詢用戶
User findUserByName(Serializable userName);
// 根據用戶名驗證
User verifyUserByName(HashMap hashMap);
// 根據手機號驗證
User verifyUserByPhone(HashMap hashMap);
}
4.4.3 編寫UserMapper.xml
位置/src/resources/mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace是指明Mybatis掃描的目錄,mapper是我們的Dao層的映射目錄 -->
<mapper namespace="cn.semiwarm.api.mapper.UserMapper">
<!--增加用戶-->
<insert id="add" parameterType="cn.semiwarm.api.entity.User">
INSERT INTO USER (user_id, user_name, user_phone, password, status, create_at)
VALUES (#{userId}, #{userName}, #{userPhone}, password(#{password}), #{status}, #{createAt})
</insert>
<!--刪除用戶-->
<delete id="delete" parameterType="cn.semiwarm.api.entity.User">
DELETE FROM USER
WHERE user_id = #{userId}
</delete>
<!--更新用戶-->
<update id="update" parameterType="cn.semiwarm.api.entity.User">
UPDATE USER
SET
user_name = #{userName},
user_phone = #{userPhone},
password = password(#{password}),
status = #{status}
WHERE user_id = #{userId}
</update>
<!--根據id查找用戶-->
<select id="findById" parameterType="java.lang.Long" resultType="cn.semiwarm.api.entity.User">
SELECT *
FROM USER
WHERE user_id = #{userId}
</select>
<!--查找所有用戶-->
<select id="findAll" resultType="cn.semiwarm.api.entity.User">
SELECT *
FROM USER
</select>
<!--根據手機號查詢用戶-->
<select id="findUserByPhone" parameterType="String" resultType="cn.semiwarm.api.entity.User">
SELECT *
FROM USER
WHERE user_phone = #{userPhone}
</select>
<!--根據用戶名稱查詢用戶-->
<select id="findUserByName" parameterType="String" resultType="cn.semiwarm.api.entity.User">
SELECT *
FROM USER
WHERE user_name = #{userName}
</select>
<!--根據用戶名驗證-->
<select id="verifyUserByName" parameterType="hashmap" resultType="cn.semiwarm.api.entity.User">
SELECT *
FROM USER
WHERE user_name = #{userName} AND password = password(#{password})
</select>
<!--根據手機號驗證-->
<select id="verifyUserByPhone" parameterType="hashmap" resultType="cn.semiwarm.api.entity.User">
SELECT *
FROM USER
WHERE user_phone = #{userPhone} AND password = password(#{password})
</select>
</mapper>
4.4.4 編寫測試類
位置/usr/test/java
import cn.semiwarm.api.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
/**
* UserMapper測試類
* 帶有swagger2的單元測試需要添加@WebAppConfiguration注解
* Created by alibct on 2017/3/6.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:spring/spring-*.xml")
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testFindAll(){
System.out.println(userMapper.findAll());
}
}
這里注意,如果是集成了Swagger2之后做單元測試的,一定要添加@WebAppConfiguration
注解否則會報以下錯誤:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
因為篇幅原因我只測試了一個方法,其它的方法請自行測試
4.4.5 編寫UserService.java接口
包cn.semiwarm.api.service
接口中主要聲明了一些復雜的業務
package cn.semiwarm.api.service;
import cn.semiwarm.api.entity.User;
import java.util.List;
/**
* 用戶服務接口
* Created by alibct on 2017/2/24.
*/
public interface UserService extends BaseService<User> {
// 獲取所有用戶
List<User> getAllUsers();
// 根據手機號獲取用戶信息
User getUserByPhone(String userPhone);
// 根據用戶名獲取用戶信息
User getUserByName(String userName);
// 注冊用戶
String signUp(User user);
}
4.4.6 編寫UserServiceImpl.java文件
包cn.semiwarm.api.service.impl
package cn.semiwarm.api.service.impl;
import cn.semiwarm.api.entity.User;
import cn.semiwarm.api.entity.UserInfo;
import cn.semiwarm.api.mapper.UserInfoMapper;
import cn.semiwarm.api.mapper.UserMapper;
import cn.semiwarm.api.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* 用戶服務具體實現類
* Created by alibct on 2017/2/24.
*/
@Service("userService")
public class UserServiceImpl implements UserService {
// 使用構造函數注入UserMapper接口
private final UserMapper userMapper;
private final UserInfoMapper userInfoMapper;
@Autowired
public UserServiceImpl(UserMapper userMapper, UserInfoMapper userInfoMapper) {
this.userMapper = userMapper;
this.userInfoMapper = userInfoMapper;
}
public List<User> getAllUsers() {
return userMapper.findAll();
}
public User getUserByPhone(String userPhone) {
return userMapper.findUserByPhone(userPhone);
}
public User getUserByName(String userName) {
return userMapper.findUserByName(userName);
}
/**
* 注冊用戶
* 向User表插入數據后必須還要向UserInfo表插入數據
*
* @param user 用戶信息
* @return 結果信息
*/
public String signUp(User user) {
Long userId = new Date().getTime();
user.setUserId(userId);
user.setStatus(true);
UserInfo userInfo = new UserInfo();
userInfo.setUserId(userId);
if (userMapper.add(user) > 0 && userInfoMapper.add(userInfo) > 0){
return "注冊成功!";
}
return "注冊失?。?;
}
}
4.4.7 編寫UserController.java文件
包cn.semiwarm.api.controller
package cn.semiwarm.api.controller;
import cn.semiwarm.api.entity.User;
import cn.semiwarm.api.service.impl.UserServiceImpl;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用戶前端控制器
* Created by alibct on 2017/2/28.
*/
@Controller
@RequestMapping(value = "/v1.0")
public class UserController {
private final UserServiceImpl userService;
@Autowired
public UserController(UserServiceImpl userService) {
this.userService = userService;
}
/**
* 獲取用戶列表
* url:www.semiwarm.cn/api/v1.0/users
*
* @return 用戶列表信息
*/
@RequestMapping(value = "/users", method = RequestMethod.GET, produces = {"application/json;charset=utf-8"})
@ApiOperation(value = "獲取用戶列表")
public
@ResponseBody
List<User> getAllUsers() {
return userService.getAllUsers();
}
/**
* 根據手機號獲取用戶信息
* url:www.semiwarm.cn/api/v1.0/users/phone/{phone}
*
* @param phone 手機號
* @return 用戶信息
*/
@RequestMapping(value = "/users/phone/{phone}", method = RequestMethod.GET, produces = {"application/json;charset=utf-8"})
@ApiOperation(value = "根據手機號獲取用戶信息")
@ResponseBody
User getUserByPhone(@PathVariable("phone") String phone) {
return userService.getUserByPhone(phone);
}
/**
* 根據用戶名獲取用戶信息
* url:www.semiwarm.cn/api/v1.0/users/name/{name}
*
* @param name 用戶名
* @return 用戶信息
*/
@RequestMapping(value = "/users/name/{name}", method = RequestMethod.GET, produces = {"application/json;charset=utf-8"})
@ApiOperation(value = "根據用戶名獲取用戶信息")
@ResponseBody
User getUserByName(@PathVariable("name") String name) {
return userService.getUserByName(name);
}
/**
* 注冊用戶
* url:www.semiwarm.cn/api/v1.0/users
*
* @param user 用戶信息
* @return 受影響的行數
*/
@RequestMapping(value = "/users", method = RequestMethod.POST, produces = {"application/json;charset=utf-8"})
@ApiOperation(value = "注冊用戶")
@ResponseBody
String signUp(@RequestBody User user) {
return userService.signUp(user);
}
}
至此結構就寫完了。
調用關系:
用戶發送請求:http://localhost:8080/v1.0/users
第一個收到請求的是前端控制器,前端控制器調用處理器映射器,處理器映射調用處理器適配器,處理器適配器調用處理器,也就是我們這里的UserController.java
處理器根據具體的路徑查找能處理該請求的方法,即getAllUsers()
方法
getAllUsers()
方法調用UserServiceImpl.java
中的getAllUsers()
方法
然后執行具體的業務邏輯,這里UserServiceImpl.java
中有調用了UserMapper
所以說之前準備的一切都是為了解耦,通過層層調用的關系將模塊獨立起來。
最后的運行結果如下圖:
