半暖商城第一季--SpringMVC+MyBatis+Swagger2配置本地應用程序接口開發環境(五)

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
所以說之前準備的一切都是為了解耦,通過層層調用的關系將模塊獨立起來。

最后的運行結果如下圖:

調用成功
調用成功

GitHub項目地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容