当前位置: 移动技术网 > IT编程>开发语言>Java > 第一个SpringBoot程序

第一个SpringBoot程序

2018年10月04日  | 移动技术网IT编程  | 我要评论

第一个springboot程序

使用idea新建工程,选择springboot initializr,勾选web一路next就搭建了一个最简单的springboot工程。如下:

package com.shy.springboot;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;

@springbootapplication
public class springbootapplication {

    public static void main(string[] args) {
        springapplication.run(springbootapplication.class, args);
    }
}

@springbootapplication整合了三个常用的注解,分别是:

  • @componentscan:会自动扫描指定包下的全部标有@component的类,并注册成bean,当然包括@component下的子注解@service,@repository,@controller;
  • @springbootconfiguration:可以当成spring的标准配置注解@configuration来使用。而@configuration表明这是一个javaconfig配置类。通常配合@bean注解,@bean注解告诉spring这个方法将返回一个对象,该对象将会注册为spring应用上下文中的bean;
  • @enableautoconfiguration:能够自动配置spring的上下文,试图猜测和配置你想要的bean类,通常会自动根据你的类路径和你的bean定义自动配置。

配置文件相关

springboot的配置文件可以使用xml和yml格式,比如使用yml格式

# 自定义属性
cupsize: b
age: 18
# 可以在yml里通过${}来引用
content: "cupsize: ${cupsize}, age: ${age}"

# 指定端口为8080,(不配置默认8080)
server:
  port: 8080

可以使用注解@value("${...}")获取配置文件中的值,@value和@autowired注解作用类似。

spring提供了两种在运行时求值的方式:

  • 属性占位符:${...}
  • spring表达式语言(spel):#{...}

如果cupsize和age都是属于同一类属性下的子属性,比如都属于girl。

那么可以写成下面的形式:

girl:
  cupsize: b
  age: 18

在java中注入时,也不用一个个属性注入,可以注入girl的全部属性。不过需要将girl的属性抽象成一个java类。

package com.shy.springboot.config;

import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.stereotype.component;



/**
 * 读取配置文件的信息并自动封装成实体类
 * 注入springboot配置文件中前缀是"girl"的全部属性
 */
@component
@configurationproperties(prefix = "girl")
public class girlproperties {
    private string cupsize;
    private integer age;

    public string getcupsize() {
        return cupsize;
    }

    public integer getage() {
        return age;
    }
}

属性配置方式

  • @value,从配置文件中注入属性
  • @configurationproperties(prefix = "...")读取配置文件中对应前缀中的属性,并映射成对象实体

环境配置:

可以建立多个application-xxx.yml文件,然后在application.yml文件中配置其中一个环境.

比如我有application-dev.yml文件表示开发环境下的配置文件,application-prod.yml文件表示生产环境下的配置文件。那么再按application.yml中配置如下

spring:
  profiles:
    active: prod

就表示使用application-dev.yml中的配置。

一些常用注解

  • controller,作用于类上,表示mvc中的控制层,可以被@componentscan扫描到并注入。用于处理http请求,返回字符串代表的模板,如xx.jsp, xx.ftl。
  • @restcontroller,是@responsebody和@controller的整合,处理http请求,可以返回实体对象或字符串,以json格式表示。
  • @resqustmapping,配置url映射。可以在类上使用(作为类中方法的前缀),可以在方法上使用。
package com.shy.springboot.controller;

import com.shy.springboot.config.girlproperties;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.restcontroller;

@restcontroller
@requestmapping("/girl")
public class hello {
    /**
     * 表示value中的值都可作为url路径,如果不指定请求方法method,那么get和post方式都可以,但是一般不推荐 
     */
    @requestmapping(value = {"/hello", "/hi"}, method = requestmethod.get)
    public string hello() {
        return "hello";
    }
}
  • @pathvariable,获取url路径中的数据
  • @requstparam,获取请求参数中的值
  • @getmapping,组合注解,是@requestmapping(value = "...", method = requestmethod.get)的缩写形式,当然也有postmapping了。
package com.shy.springboot.controller;

import com.shy.springboot.config.girlproperties;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.*;

@restcontroller
@requestmapping("/girl")
public class hello {
    // 可以响应 http://localhost:8080/girl/hello/xx
    @requestmapping(value = {"/hello/{id}"}, method = requestmethod.get)
    public string hello(@pathvariable("id") integer id) {
        return "my id is " + id;
    }
    // 可以响应 http://localhost:8080/girl/hello?id=xx
    // required = false表示这个参数可以为空,defaultvalue表示当参数为空时的默认值,因此访问http://localhost:8080/girl/hello,将使用默认值1。
    @requestmapping(value = {"/hello"}, method = requestmethod.get)
    public string hello2(@requestparam(value = "id", required = false,defaultvalue = "1") integer id) {
        return "my id is " + id;
    }
}
 

数据库配置

本例子使用jpa和mysql,所以在pom中加入如下依赖

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-data-jpa</artifactid>
</dependency>
<dependency>
    <groupid>mysql</groupid>
    <artifactid>mysql-connector-java</artifactid>
</dependency>

在application.yml中配置数据源和jpa相关。

spring:
  profiles:
    active: dev
  # 以下使用了jpa和mysql  
  datasource:
    driver-class-name: com.mysql.jdbc.driver
    url: jdbc:mysql://127.0.0.1:3306/dbgirl
    username: root
    password: admin
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true

jpa(java persistence api),即java持久化api,hibernate实现了这个规范。

package com.shy.springboot.database;

import javax.persistence.entity;
import javax.persistence.generatedvalue;
import javax.persistence.id;

@entity
public class girl {
    @id
    @generatedvalue(strategy = generationtype.identity)
    private integer id;
    private integer age;
    private string cupsize;

    public integer getid() {
        return id;
    }

    public void setid(integer id) {
        this.id = id;
    }

    public integer getage() {
        return age;
    }

    public void setage(integer age) {
        this.age = age;
    }

    public string getcupsize() {
        return cupsize;
    }

    public void setcupsize(string cupsize) {
        this.cupsize = cupsize;
    }

    public girl() {
    }
}
  • @entity 表示这是个实体类,可以映射成数据表。
  • @id表示该属性为主键
  • @generatedvalue表示该属性字段为自增,一般搭配@id使用

@generatedvalue有几种策略

  • identity:采用数据库id自增长的方式来自增主键字段,oracle 不支持这种方式,使用mysql时,配置该策略可以实现主键自增。
  • auto:jpa自动选择合适的策略,是默认选项;
  • sequence:通过序列产生主键,通过@sequencegenerator 注解指定序列名,mysql不支持这种方式 ;
  • table:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植;

jpa.hibernate.ddl-auto,共有五种配置方式。

  • ddl-auto:create: 每次运行该程序,没有表格会新建表格,表内有数据会清空
  • ddl-auto:create-drop: 每次程序结束的时候会清空表
  • ddl-auto:update: 每次运行程序,没有表格会新建表格,若已经存在表格,则只会更新
  • ddl-auto:validate: 运行程序会校验数据与数据库的字段类型是否相同,不同会报错
  • none: 禁止ddl处理

controller和几个简单的请求

repository提供了最基本的数据访问功能,通过新建一个接口继承jparepository<t, id>,可以直接使用接口中现成的方法来实现对数据的访问。

package com.shy.springboot.database;

import org.springframework.data.jpa.repository.jparepository;

import java.util.list;

public interface girlrepo extends jparepository<girl, integer> {
    // 自定义的查询方法,方法名要严格按照一定规则来命名
    list<girl> findbyage(integer age);
}

泛型中的girl表示该repository可以访问由girl映射的数据表,integer表示id的数据类型。

写一个controller,处理各种请求来看jpa是如何与数据库交互的。

package com.shy.springboot.controller;

import com.shy.springboot.database.girl;
import com.shy.springboot.database.girlrepo;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.*;

import java.util.list;

@restcontroller
public class girlcontroller {

    @autowired
    private girlrepo girlrepo;

    /**
     * 查询所有女生
     * @return
     */
    @getmapping("/girls")
    public list<girl> girls() {
        return girlrepo.findall();
    }

    /**
     * 添加一个女生
     * @param cupsize
     * @param age
     * @return
     */
    @postmapping("/addgirl")
    public girl addgirl(@requestparam("cupsize") string cupsize,
                        @requestparam("age") integer age) {

        girl girl = new girl();
        girl.setage(age);
        girl.setcupsize(cupsize);
        return girlrepo.save(girl);
    }

    /**
     * 通过id更新一个女生
     * @param id
     * @param cupsize
     * @param age
     * @return
     */
    @postmapping("/updategirl/{id}")
    public girl updategirl(@pathvariable("id") integer id,
                           @requestparam("cupsize") string cupsize,
                           @requestparam("age") integer age) {

        girl girl = new girl();
        girl.setid(id);
        girl.setage(age);
        girl.setcupsize(cupsize);
        return girlrepo.save(girl);
    }

    /**
     * 根据id删除一个女生
     * @param id
     */
    @getmapping("/deletegirl/{id}")
    public void deletegirl(@pathvariable("id") integer id) {
        girl girl = new girl();
        girl.setid(id);
        girlrepo.delete(girl);
    }

    /**
     * 根据id查询一个女生
     * @param id
     * @return
     */
    @getmapping("girls/{id}")
    public girl girlfindone(@pathvariable("id") integer id) {
        return girlrepo.findbyid(id).get();
    }

    /**
     * 根据年龄查询一个女生
     * @param age
     * @return
     */
    @getmapping("girls/age/{age}")
    public list<girl> findgirlsbyage(@pathvariable("age") integer age) {
        return girlrepo.findbyage(age);
    }
}

没有写一句sql语句,就完成了对girl表的增删改查,用起来还是很舒服的。

事务管理

下面的inserttwo方法插入两条数据,如果不进行事务管理,则插入girla成功,插入girlb失败。加上@transactional注解后(有两个同名注解,导入spring的),要么两条数据都插入成功,要么两条都插入失败。因为在本例中会出现异常,所以两条都插入失败。

// service中
package com.shy.springboot.service;

import com.shy.springboot.database.girl;
import com.shy.springboot.database.girlrepo;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;

@service
public class girlservice {
    @autowired
    private girlrepo girlrepo;
    @transactional
    public void inserttwo() {
        girl girla = new girl();
        girla.setcupsize("b");
        girla.setage(18);
        girlrepo.save(girla);
        // 除0异常,退出
        int a = 3 / 0;
        girl girlb = new girl();
        girlb.setcupsize("c");
        girlb.setage(20);
        girlrepo.save(girlb);
    }
}

// controller中
@postmapping("/girls/inserttwo")
    public void inserttwo() {
        girlservice.inserttwo();
}

因为hibernate创建的表默认引擎是myisam,所以如果发现事务没有作用,要手动修改引擎为innodb。

alter table xxx engine=innodb;

表单验证

在上面的例子中如果要对年龄作限制,比如小于18岁的girl不能添加。可以在实体类中对其中的字段属性使用注解来加以限制。

@min(value = 18, message = "未满18岁不得入内!")
private integer age;

这句代码限制了girl的年龄不能低于18岁。在controller中修改添加女生的逻辑

/**
 * 添加一个女生
 * @return
 */
@postmapping("/addgirl")
public girl addgirl(@valid girl girl, bindingresult result) {
    if (result.haserrors()) {
        system.out.println(result.getfielderror().getdefaultmessage());
        return null;
    }
    return girlrepo.save(girl);
}

@valid可以对对象进行验证,加了@valid注解的参数,其后要紧跟着bindingresult或者errors(前者是后者的实现类),用于保存验证结果。如果对象中有属性不满足验证条件,其结果将体现中bindingresult中。

aop

首先在pom中添加依赖

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-aop</artifactid>
</dependency>

然后编写切面

package com.shy.springboot.aspect;

import org.aspectj.lang.annotation.after;
import org.aspectj.lang.annotation.aspect;
import org.aspectj.lang.annotation.before;
import org.aspectj.lang.annotation.pointcut;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.stereotype.component;

@aspect
@component
public class httpaspect {
    private static final logger log = loggerfactory.getlogger(httpaspect.class);

    @pointcut("execution(public * com.shy.springboot.controller.girlcontroller.*(..))")
    public void log() {}

    @before("log()")
    public void dobefore() {
        log.info("我在方法调用前执行");
    }

    @after("log()")
    public void doafter() {
        log.info("我在方法调用后执行");
    }

}

因为在方法调用的前后都要对相同的方法进行通知,为了避免代码冗余,把@before和@after的execution表达式抽取成切点。

@pointcut("execution(public * com.shy.springboot.controller.girlcontroller.*(..))")

表示对girlcontroller中所有public的任意返回值、任意参数的方法进行通知。注意该注解需要用在方法上,所以public log() {}在这里只是起一个标识作用,供@pointcut依附,所以它的方法体是空的。

该切面使用了slf4j的日志。当请求http://localhost:8080/addgirl时,控制台输出以下日志,可以显示比system.out.println()更详细的信息。

2018-10-03 10:03:46.899  info 1892 --- [nio-8080-exec-3] com.shy.springboot.aspect.httpaspect     : 我在方法调用前执行
hibernate: insert into girl (age, cup_size) values (?, ?)
2018-10-03 10:03:47.031  info 1892 --- [nio-8080-exec-3] com.shy.springboot.aspect.httpaspect     : 我在方法调用后执行

输出的hibernate: insert into girl (age, cup_size) values (?, ?)表示了controller中addgirl方法的执行,在其前后分别输出了@before和@after执行的逻辑,所以aop确实是生效了的。

现在修改dobefore方法,使它能从request域中获取请求url、ip地址、请求方法、请求中传递的参数。

@aspect
@component
public class httpaspect {
    private static final logger log = loggerfactory.getlogger(httpaspect.class);

    @pointcut("execution(public * com.shy.springboot.controller.girlcontroller.*(..))")
    public void log() {}

    @before("log()")
    public void dobefore(joinpoint joinpoint) {
        servletrequestattributes attributes = (servletrequestattributes) requestcontextholder.getrequestattributes();
        httpservletrequest request = attributes.getrequest();
        // url
        log.info("url={}", request.getrequesturi());
        // ip
        log.info("ip={}", request.getremoteaddr());
        // method
        log.info("method={}", request.getmethod());
        // 参数
        log.info("args={}", joinpoint.getargs());
        // class-method
        log.info("class_method={}", joinpoint.getsignature().getdeclaringtypename() + " " + joinpoint.getsignature().getname());

        log.info("我在方法调用前执行");
    }

    @afterreturning(value = "log()",returning = "obj")
    public void doafterreturning(object obj) {
        if (obj != null) {
            log.info("girl={}", obj.tostring());
        }
    }

    @after("log()")
    public void doafter() {
        log.info("我在方法调用后执行");
    }

}

在通知方法中可以声明一个joinpoint类型的参数,通过joinpoint可以访问连接点的细节。

  • getargs():获取连接点方法运行时的入参列表;
  • getsignature() :获取连接点的方法签名对象;
  • getsignature().getname():获取连接点的方法名
  • getsignature().getdeclaringtypename():获取连接点所在类的名称

还新增了一个@afterreturning的通知,在方法成功返回后执行(若抛出异常将不会执行该通知),和@after的区别在于:被增强的方法不论是执行成功还是抛出异常,@after通知方法都会得到执行。

aop中 @before @after @afterthrowing @afterreturning的执行顺序如下:

public object invoke(object proxy, method method, object[] args) throws throwable {
   object result;
   try {
       // @before
       result = method.invoke(target, args);
       // @after
       return result;
   } catch (invocationtargetexception e) {
       throwable targetexception = e.gettargetexception();
       // @afterthrowing
       throw targetexception;
   } finally {
       // @afterreturning
   }
}

可知@afterreturning的执行在@after之后。

如果请求http://localhost:8080/addgirl,将输出以下日志(日志一些无关紧要的内容已被删除)

url=/addgirl
ip=0:0:0:0:0:0:0:1
method=post
args=girl{id=null, age=26, cupsize='c'}
class_method=com.shy.springboot.controller.girlcontroller addgirl
我在方法调用前执行
hibernate: insert into girl (age, cup_size) values (?, ?)
我在方法调用后执行
girl=girl{id=27, age=26, cupsize='c'}

统一异常处理

前面的addgirl方法,当验证不通过时,返回null并在控制台打印相关信息;当验证通过又返回girl。返回值不统一,而且如果我们希望将错误信息显示在页面,怎么办呢?

可定义一个result<t>,将要呈现的信息统一化,分别是错误码code,错误信息msg和承载的对象t,这样不管是成功还是发生各种各样的异常,都可以返回统一的result对象。

package com.shy.springboot.domain;

public class result<t> {
    /** 错误码 */
    private integer code;
    /** 信息 */
    private string msg;
    /** 对象 */
    private t data;

    public integer getcode() {
        return code;
    }

    public void setcode(integer code) {
        this.code = code;
    }

    public string getmsg() {
        return msg;
    }

    public void setmsg(string msg) {
        this.msg = msg;
    }

    public t getdata() {
        return data;
    }

    public void setdata(t data) {
        this.data = data;
    }
}

再写一个工具类,可在成功和异常时候设置对应的状态和信息,可有效减少重复代码。

package com.shy.springboot.util;

import com.shy.springboot.domain.result;

public class resultutil {
    public static result success(object obj) {
        result result = new result();
        result.setmsg("成功");
        result.setcode(0);
        result.setdata(obj);
        return result;
    }

    public static result success() {
        return success(null);
    }

    public static result error(integer code, string msg) {
        result result = new result();
        result.setmsg(msg);
        result.setcode(code);
        return result;
    }
}

于是我们的addgirl方法可以重构成下面的样子

@postmapping("/addgirl")
public result<girl> addgirl(@valid girl girl, bindingresult bindingresul) {
    if (bindingresul.haserrors()) {
        return resultutil.error(1,bindingresul.getfielderror().getdefaultmessage());
    }
    girlrepo.save(girl);
    return resultutil.success(girl);
}

现在新增一个检查年龄的逻辑,小于14岁的认为在上小学,14~17岁认为在上初中,这两种情况都不允许其进入,当检查到年龄不符合要求时,抛出异常。

在girlservice中

public void checkage(integer id) {
    girl girl = girlrepo.findbyid(id).get();
    int age = girl.getage();
    if (age < 14) {
        throw new girlexception(resultenum.primary_school);
    } else if (age < 17) {
        throw new girlexception(resultenum.middle_school);
    }
    // 其他年龄的逻辑处理
}

注意上面使用枚举来统一管理各种code对应的msg。

package com.shy.springboot.enums;

public enum resultenum {
    success(0, "成功"),
    error(-1, "未知错误"),
    primary_school(100, "你可能还在上小学"),
    middle_school(101, "你可能还在上初中");

    private integer code;
    private string msg;

    resultenum(integer code, string msg) {
        this.code = code;
        this.msg = msg;
    }

    public integer getcode() {
        return code;
    }

    public string getmsg() {
        return msg;
    }
}

girlexception是个自定义异常类,除了message还把code整合进去了。

package com.shy.springboot.exception;

import com.shy.springboot.enums.resultenum;

public class girlexception extends runtimeexception{
    private integer code;

    public girlexception(resultenum resultenum) {
        super(resultenum.getmsg());
        this.code = resultenum.getcode();
    }

    public integer getcode() {
        return code;
    }

    public void setcode(integer code) {
        this.code = code;
    }
}

在controller中只是简单调用下service中的方法而已

@getmapping("/girlage/{id}")
public void getage(@pathvariable("id") integer id) {
    girlservice.checkage(id);
}

如果现在启动程序,请求http://localhost:8080/girlage/22, 将按照自定义异常,但是返回的结果其格式是下面这样的:

{
    timestamp: 14xxxxxxx,
    status: 500,
    exception: xxx,
    message: xxx,
    path: "/girlage/22"
}

因为系统内部发生了错误,不断往上抛异常就会得到上面的信息。如果要保持不管在什么情况下统一返回result<t>中的信息,像下面这样:

{
    code: xxx,
    msg: xxx,
    data: xxx
}

则需要对异常做一个捕获,取出有用的message部分,然后再封装成result对象,再返回给浏览器。为此新建一个异常捕获类

package com.shy.springboot.handle;

import com.shy.springboot.domain.result;
import com.shy.springboot.exception.girlexception;
import com.shy.springboot.util.resultutil;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.web.bind.annotation.controlleradvice;
import org.springframework.web.bind.annotation.exceptionhandler;
import org.springframework.web.bind.annotation.responsebody;

/**
 * controlleradvice注解将作用在所有注解了@requestmapping的控制器的方法上。
 * 配合@exceptionhandler,用于全局处理控制器里的异常
 */
@controlleradvice
public class exceptionhandle {
    private static final logger log = loggerfactory.getlogger(exceptionhandle.class);

    @exceptionhandler(exception.class)
    @responsebody
    public result handle(exception e) {
        if (e instanceof girlexception) {
            girlexception exception = (girlexception) e;
            return resultutil.error(exception.getcode(),exception.getmessage());
        }
        log.error("系统异常:{}", e.getmessage());
        return resultutil.error(-1,"未知错误");
    }
}

该类使用了注解@controlleradvice,@controlleradvice会作用在所有注解了@requestmapping的控制器的方法上,再配合@exceptionhandler,用于全局处理控制器里的异常。@exceptionhandler(exception.class)表示可以处理exception类及其子类。

因为除了会抛出自定义异常girlexception外,还有可能因为系统原因抛出其他类型的异常(如空指针异常),因此针对不同类型的异常返回不同的状态码,上面使用了instanceof来判断异常类型。如果不是girlexception,被统一归类为未知错误,但是各种异常都显示未知错误不便于排查问题,因此在可控制台输出了异常原因来加以区分。

单元测试

springboot中进行单元测试十分便捷,springboot中默认使用了junit4。

在src/test下可以创建单元测试类,当然更简单的方法是在idea下右键,go to -> test subject,然后选择想要进行测试的方法即可。

下面的单元测试针对service层,主要是判断某数据库中某id的girl,其年龄实际值和预期值是否一致。有两个比较关键的注解

  • @runwith(springrunner.class):当一个类用@runwith注释或继承一个用@runwith注释的类时,junit将调用它所引用的类来运行该类中的测试而不是开发者去在junit内部去构建它,因此这句代码意思是让测试运行于spring测试环境中,springrunner仅仅继承了springjunit4classrunner而已,并没有扩展什么功能,前者可以看作是后者的“别名”。
  • @springboottest:可以自动搜寻@springbootconfiguration;在没有明确指定@contextconfiguration(loader=...)时,使用springbootcontextloader作为默认的contextloader,等等。
package com.shy.springboot.service;

import com.shy.springboot.domain.girl;
import com.shy.springboot.repository.girlrepo;
import org.junit.assert;
import org.junit.test;
import org.junit.runner.runwith;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.boot.test.context.springboottest;
import org.springframework.test.context.junit4.springrunner;

/**
 * runwith(springrunner.class),让测试运行于spring测试环境
 */
@runwith(springrunner.class)
@springboottest
public class girlservicetest {
    @autowired
    private girlrepo girlrepo;
    @test
    public void findone() {
        girl girl = girlrepo.findbyid(22).get();
        assert.assertequals(new integer(14), girl.getage());
    }
}

然后针对controller层,对某次请求进行测试,这里使用到了mockmvc。

package com.shy.springboot.controller;

import org.junit.test;
import org.junit.runner.runwith;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.autoconfiguremockmvc;
import org.springframework.boot.test.context.springboottest;
import org.springframework.test.context.junit4.springrunner;
import org.springframework.test.web.servlet.mockmvc;
import org.springframework.test.web.servlet.request.mockmvcrequestbuilders;
import org.springframework.test.web.servlet.result.mockmvcresultmatchers;
@runwith(springrunner.class)
@springboottest
@autoconfiguremockmvc
public class girlcontrollertest {
    @autowired
    private mockmvc mockmvc;
    @test
    public void girls() throws exception {
        mockmvc.perform(mockmvcrequestbuilders.get("/girls")).andexpect(mockmvcresultmatchers.status().isok());
        /* 下面这条测试不能通过 */
        // mockmvc.perform(mockmvcrequestbuilders.get("/girls")).andexpect(mockmvcresultmatchers.content().string("abc"));
    }
}

第一条测试模拟以get方法请求/girls,并期望状态码是200 ok。注释掉的第二条测试期望响应的内容是abc,然而我们返回的是json格式,所以肯定不能通过测试的。


2018.10.4

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网