半暖商城第一季--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阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容