目录
springboot中mapper的加载接口代理对象有两种方法
1.对SpringBoot的认识
1.1.什么是SpringBoot
1.2.SpringBoot解决了哪些痛点
1.3.SpringBoot有哪些特性
2. 入门案例
2.1.新建module
![](https://i-blog.csdnimg.cn/blog_migrate/9ac3a0776f2f76f1a67aa579671e6195.png)
![](https://i-blog.csdnimg.cn/blog_migrate/844dad211f2ede5260550672993cb41a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/80e74bebeb48bb0d76829a8e4b02b407.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d66c092549295b294a4850cfb96e6d4a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0e1922b9d04e52bf3f41834c2e34c4ac.png)
2.2.pom.xml添加相关依赖
2.2.1.添加父工程坐标
2.2.2.添加web启动器
2.2.3.管理jdk版本
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bowei.springboot</groupId>
<artifactId>springbootdemo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--管理jdk版本-->
<properties>
<java.version>11</java.version>
</properties>
<!--添加父工程坐标 我们的项目继承父工程项目,这样我们就不用操心依赖的版本冲突问题了
由parent统一管理版本号,不需要再写版本号-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<!--添加web启动器
为了让SpringBoot帮我们完成各种自动配置,我们必须引入SpringBoot提供的自动配置依赖,我们称为启动器 。
因为是web项目,这里我们引入web启动器-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
2.3.启动类
![](https://i-blog.csdnimg.cn/blog_migrate/706ca53d72ca737314e40a81b6ceab0e.png)
下面,就可以像以前那样开发SpringMVC的项目
2.4.编写controller
![](https://i-blog.csdnimg.cn/blog_migrate/a15e075b341e63d41dcdaed22b321dca.png)
2.5.启动测试
![](https://i-blog.csdnimg.cn/blog_migrate/92656b237793a65dee65f51d250f5bf7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d6324157206861ad0120b6563872f0d4.png)
3. Spring全注解配置和属性注入
上面demo没有任何的配置,就可以实现一个SpringMVC的项目了,快速、高效。
![](https://i-blog.csdnimg.cn/blog_migrate/fff0278d0a8bcd75d7f33ddb9950ccd3.png)
在Spring3.0开始,Spring官方就已经开始推荐使用java配置来代替传统的xml配置了
3.1.spring全注解配置
@Configuration :声明一个类作为配置类,代替xml文件
3.1.1.连接池在spring全注解配置中是如何配置的
下面测试spring全注解配置是如何读取到属性文件
![](https://i-blog.csdnimg.cn/blog_migrate/7dc9da2e2bc4b8e881d1eba8ed24a990.png)
在 HelloController 中测试
Debug运行并查看 属性注入成功了
这是spring的全注解配置实现的一个bean对象的创建和属性的注入,下面看下spring boot提供的属性注入方式。
3.2.SpringBoot的属性注入\读取配置完文件
将jdbc.properties改成application.properties
2)在JdbcConfig2中注入JdbcProperties对象,获取属性值给数据源,最后Bean注解将数据源反转给spring容器,将来在其他地方可以通过注入方式使用dataSource数据源。
三种方式注入JdbcProperties
![](https://i-blog.csdnimg.cn/blog_migrate/d5399ee929eee8ac7ad9d6681709ed8f.png)
debug
![](https://i-blog.csdnimg.cn/blog_migrate/578150fd53f5bd2417e24b9d556836b7.png)
上述分别通过两种@Value和@ConfigurationProperties注解来读取properties文件,给Properties类中对应的属性赋值
@ConfigurationProperties 优势:
Relaxed binding:松散绑定
3.3.springboot更优雅的注入
不再需要属性读取类,@ConfigurationProperties 这个注解是放在需要的方法上,SpringBoot会自动把相关属性通过set方法注入到bean对象中。
我们直接把 @ConfigurationProperties(prefix = "jdbc") 声明在需要使用的 @Bean 的方法上,然后SpringBoot 就会自动调用这个Bean(此处是DataSource)的set方法,然后完成注入。使用的前提是:该类必须有对应属性的set方法!
application.properties
将properties中属性值注入到dataSource
4. 自动配置原理
![](https://i-blog.csdnimg.cn/blog_migrate/781cb8e1848b17c07f2e017dc4d5fb41.png)
重点关注@SpringBootApplication注解
4.1.@SpringBootApplication
![](https://i-blog.csdnimg.cn/blog_migrate/b77d5d34aad1b48ccff8210523fae932.png)
4.2.@SpringBootConfiguration
![](https://i-blog.csdnimg.cn/blog_migrate/0802a34e42f3e000f1731d76f7e91765.png)
4.3.@ComponentScan
![](https://i-blog.csdnimg.cn/blog_migrate/a297d673118b7f8f205520ad8b3b4e2d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0ab0d4acbc44816559d0dbc5b8a1ce48.png)
4.4.@EnableAutoConfiguration
4.5.默认配置原理
![](https://i-blog.csdnimg.cn/blog_migrate/7845062836a2d3dac336cf0f489b38a2.png)
非常多,几乎涵盖了现在主流的开源框架,例如:
![](https://i-blog.csdnimg.cn/blog_migrate/85533ad58d848c7066e3c63f97f84230.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8f072ab9cd38d1ecf3307298e18f2311.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ff001630d6b21e6a16d35d1ab6c1bbef.png)
![](https://i-blog.csdnimg.cn/blog_migrate/66dc31c5314cbb0b798547cf77fa74b2.png)
4.6.总结
SpringBoot为我们提供了默认配置,而默认配置生效的条件一般有两个:
5.SpringBoot整合其它框架
5.1.整合SpringMVC
5.1.1.修改端口
![](https://i-blog.csdnimg.cn/blog_migrate/9824280ef747ca0b3bd908394748db35.png)
5.1.2.访问静态资源
![](https://i-blog.csdnimg.cn/blog_migrate/58c49012171418727d692b88fcb9f2bd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/eac325da141fcdf34b957c0173dbf4e5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ffde72c5b3686256e230c4e5b9603d38.png)
![](https://i-blog.csdnimg.cn/blog_migrate/343f8d4807bf450ebdb1ebd0f669b2b1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/541f5f519253f40d5e77d9aa8a240cc4.png)
package com.cbw.interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.debug("处理器执行前执行!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
logger.debug("处理器执行后执行!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
logger.debug("跳转后执行!");
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/9238cdea9feffa6265d82ddde896a34d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2c37e2f57383c9258e30406dc19ebca5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/eb4af958d1aa1bb9116576cff059660a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bdddc4a1f43098f2632ca7ecbf83e237.png)
5.2.整合jdbc
5.2.1.引入依赖
springboot根据依赖自动的帮我们配置jdbc,不要忘了数据库驱动,SpringBoot并不知道我们用的什么数据库,这里我们选择MySQL。
![](https://i-blog.csdnimg.cn/blog_migrate/ce88644cfa2a99f0dec4ebd2f2d0c4db.png)
5.2.2.配置连接池
![](https://i-blog.csdnimg.cn/blog_migrate/4ac08bf939cd1f5276f60b2018a15116.png)
![](https://i-blog.csdnimg.cn/blog_migrate/72c2d0cf5e3337c900858248f91c6287.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0f8f135706715900707807f21c5853bf.png)
# 连接四大参数
spring.datasource.url = jdbc:mysql://localhost:3306/tb_userserverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123
# 可省略,SpringBoot自动推断
spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.minimum-idle=10
![](https://i-blog.csdnimg.cn/blog_migrate/4b00eff87cc6e387dc9c662e4dbc825a.png)
持久层 这里使用的是原始的dao
测试
数据库
5.3.整合mybatis
![](https://i-blog.csdnimg.cn/blog_migrate/312ffe2c95fa9773472e17f6c8302892.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f8db6e31b77889ed39b4431c3772f839.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d2ff2105dba459185608c9862c5d7ace.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3e9e5430595ad364a56b40fd8135b9e3.png)
mapper接口
mapper接口映射文件
mybatis中生成mapper接口代理对象有两种方式
1.sqlSession.getMapper(UserMapper.class)
2.mybatis反向生成
springboot中mapper的加载接口代理对象有两种方法
1.使用@Mapper注解(不推荐)
2.设置MapperScan注解扫描包(推荐)
![](https://i-blog.csdnimg.cn/blog_migrate/bc85c24e256789a7e87b6d61d601308e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a3ad39c16bbb444a0c1f2457a10b551f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1e7ac5b9c6669e0a9baa16c9fe4c0645.png)
5.4.通用mapper tk mybatis
概念
通用Mapper的作者也为自己的插件编写了启动器,我们直接引入即可:
实体类
tk mybatis 实体类使用的注解是jpa注解
注意事项:
1. 默认表名=类名,字段名=属性名
2. 表名可以使用 @Table(name = "tableName") 进行指定
3. @Column(name = "fieldName") 指定
![](https://i-blog.csdnimg.cn/blog_migrate/8114906884da179297e19ab0e6c25188.png)
一旦继承了Mapper,继承的Mapper就拥有了Mapper所有的通用方法:
当然,我们也可以自定义sql映射,那么上面的mapper接口中就需要写接口方法了,而且自定义的sql映射是不支持字段下划线user_name转驼峰userName的,而且字段名和属性名不一致时需要在sql中通过起别名,变成一致,而tk mybatis提供的内置方法是支持下划线转驼峰的,另外如果字段和属性不一致,只需要在属性上userName添加一个注解Column(name = "user_name")即可。
Select
![](https://i-blog.csdnimg.cn/blog_migrate/4150bb9753a62b8ec880ed6596fc4908.png)
![](https://i-blog.csdnimg.cn/blog_migrate/613dafee35abd6d492afccba798749d3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ce723013ee57df827383c37e8d2f65be.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c3cb5c2b85784682ae4a20eb05dc347f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/42b64b87d5d62aed75326518b5011049.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ec3ff444d16584fbac035df5958eb038.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e3ba7eb8e18ecbd0622e9c930e7c83f3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c87b72fb5c6a66293a08123ff47e120e.png)
上述只有第一个方法使用的是自定义的sql,要是没有自定义sql,那么映射文件就不需要了,而且mapper接口只需要实现tk mybatis的mapper接口就可以,也不需要写接口方法了。
6.Thymeleaf [taim li:f]
6.1.什么是Thymeleaf
6.2.springBoot整合Thymeleaf
6.2.1.引入Thymeleaf启动器
SpringBoot会自动为Thymeleaf注册一个视图解析器:
一般我们无需进行修改,默认即可
编写html模板,渲染模型中的数据:
语法
${} :这个类似与el表达式,但其实是ognl的语法,比el表达式更加强大
th- 指令: th- 是利用了Html5中的自定义属性来实现的。如果不支持H5,可以用 data-th- 来代替
th:each :类似于 c:foreach 遍历集合,但是语法更加简洁
th:text :声明标签中的文本 例如 <td th-text='${user.id}'>1</td> ,如果user.id有值,会覆盖默认的1 如果没有值,则会显示td中默认的1。这正是thymeleaf能够动静结合的原因,模板解析失败不影响 页面的显示效果,因为会显示默认值!
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8">
<title>首页</title>
<style type="text/css">
table {border-collapse: collapse; font-size: 14px; width: 80%; margin: auto}
table, th, td {border: 1px solid darkslategray;padding: 10px} </style>
</head>
<body>
<div style="text-align: center">
<span style="color: darkslategray; font-size: 30px">欢迎光临!</span> <hr/>
<table class="list">
<tr>
<th>id</th>
<th>姓名</th>
<th>用户名</th>
<th>年龄</th>
<th>性别</th>
<th>生日</th>
<th>备注</th>
<th>操作</th>
</tr>
<tr th:each="user, status : ${users}" th:object="${user}">
<td th:text="${user.id}">1</td>
<td th:text="*{name}">张三</td>
<td th:text="*{userName}"></td>
<td th:text="${user.age}">zhangsan</td>
<td th:text="${user.sex} == 1 ? '男': '女'">男</td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">2021-02-30</td>
<td th:text="${user.note}">1</td>
<td><a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
</td>
</tr>
</table>
</div>
</body>
</html>
6.2.2.模板缓存
![](https://i-blog.csdnimg.cn/blog_migrate/d6735371fbcc0f5b667db91a0369b683.png)
6.3.thymeleaf详解
6.3.1.thymeleaf表达式
![](https://i-blog.csdnimg.cn/blog_migrate/cd05061457ef83cfe357677c97485988.png)
![](https://i-blog.csdnimg.cn/blog_migrate/59a7bc040874f938980430efddba290a.png)
<a th:href="'/approve/' + ${user.id}">审核</a>
6.3.2.表达式常见用法
字面(Literals)
文本文字(Text literals): 'one text', 'Another one!',…
数字文本(Number literals): 0, 34, 3.0, 12.3,…
布尔文本(Boolean literals): true, false
空(Null literal): null
文字标记(Literal tokens): one, sometext, main,…
文本操作(Text operations)
字符串连接(String concatenation):
文本替换(Literal substitutions): |The name is ${name}|
算术运算(Arithmetic operations)
二元运算符(Binary operators): +, -, *, /, %
减号(单目运算符)Minus sign (unary operator): -
布尔操作(Boolean operations)
二元运算符(Binary operators): and, or
布尔否定(一元运算符)Boolean negation (unary operator): !, not
比较和等价(Comparisons and equality)
比较(Comparators): >, <, >=, <= (gt, lt, ge, le)
等值运算符(Equality operators): ==, != (eq, ne)
条件运算符(Conditional operators)
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
6.3.3.常用th标签
6.3.4.th标签基本用法
1. 赋值、字符串拼接
字符串拼接还有另外一种简洁的写法
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
2. 条件判断 If/Unless
Thymeleaf中使用th:if和th:unless属性进行条件判断
th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容
也可以使用 (if) ? (then) : (else) 这种语法来判断显示的内容
<h5>if指令</h5>
<a th:if="${users.size() > 0}">查询结果存在</a><br>
<a th:if="${users.size() <= 0}">查询结果不存在</a><br>
<a th:unless="${session.user != null}" href="#">登录</a><br>
3. for 循环
status称作状态变量,属性有
index:当前迭代对象的index(从0开始计算)
count: 当前迭代对象的index(从1开始计算)
size:被迭代对象的大小
current:当前迭代变量
even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算)
first:布尔值,当前循环是否是第一个
last:布尔值,当前循环是否是最后一个
4. 内联文本
内联文本:[[…]]内联文本的表示方式,使用时,必须先用th:inline=”text/javascript/none”激活
th:inline="text" 内联文本
th:inline="javascript" 内联js
th:inline="none" 关闭内联
th:inline可以在 父级标签内使用,甚至作为body的标签
内联文本尽管比th:text的代码少,不利于原型显示
在thymeleaf指令中显示
<h6 th:text="${text}">静态内容</h6>
使用内联文本显示model attribute
5. 内联js
6. 内嵌变量
为了模板更加易用,T
hymeleaf还提供一系列Utility对象(内置Context中),可以通过#直接访问:
dates : java.util.Date**的功能方法类。
calendars : 类似#dates,面向java.util.Calendar
numbers : 格式化数字的功能方法类
strings : 字符串对象的功能类,
contains,startWiths,prepending/appending等等。
objects: 对objects的功能类操作。
bools: 对布尔值求值的功能方法。
arrays:对数组的功能类方法。
lists: 对lists功能类方法
sets
maps …
比如
6.3.5.thymeleaf布局
在/resources/templates/目录下创建footer.html,内容如下
编写一个片段,在其他页面引用
在页面任何地方引入:
th:insert :保留自己的主标签,保留th:fragment的主标签。
th:replace :不要自己的主标签,保留th:fragment的主标签。
th:include :保留自己的主标签,不要th:fragment的主标签。(官方3.0后不推荐)
这几个标签的区别,查看源码
7. Mybatis Plus
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,避免了我 们重复CRUD语句。
7.1.创建工程,引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-boot-mybatis-plus</artifactId>
<version>1.0-SNAPSHOT</version>
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<mybatisplus.version>3.3.2</mybatisplus.version>
<skipTests>true</skipTests>
</properties>
<!--springboot启动器-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!--mp启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<!--测试-->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<!--简化实体类开发,不需要set get方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
7.2.配置文件application.yml
在Springboot中,推荐使用properties或者YAML文件来完成配置,但是对于较复杂的数据结构来说,YAML又远远 优于properties。
看一个Springboot中的properties文件和对应YAML文件的对比
可以明显的看到,在处理层级关系的时候,properties需要使用大量的路径来描述层级(或者属性),比如 environments.dev.url和environments.dev.name。其次,对于较为复杂的结构,比如数组(my.servers),写起 来更为复杂。而对应的YAML格式文件就简单很多:
数据库脚本文件/db/data-h2.sql和/db/schema-h2.sql
h2数据库是一个基于内存的数据库,在jvm启动时,自动执行脚本加载相应的数据,常用于开发时测试使用。
springboot 中使用h2数据库直接按照上面配置,配置schema表结构脚本和data数据脚本即可
这里用户名密码可以省略不写,或者随意设定
7.3.springboot启动类
7.4.实体类
7.5.mapper接口
测试
7.6.spring-boot-mp注解
注解解决表与实体类不一致问题
MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹 配
mybatis plus注解策略配置
1.如果mysql自增主键注解策略设置如下
@TableId(type = IdType.AUTO)
private Long id;
2.默认主键策略
ASSIGN_ID(3), //采用雪花算法生成全局唯一主键
3.排除实体类中非表字段
使用 @TableField(exist = false) 注解
7.7.spring-boot-mp内置CRUD
7.7.1.查询所有 根据id/条件查询
7.7.2.插入
7.7.3.删除&批量删除
7.7.4.更新&批量更新
7.8.spring-boot-mp分页
7.8.0.分页原理
通过分页拦截器拦截查询语句的执行,使用count语句解析器对left join这种一对一关联的sql进行优化,当分页查询的时候,将count查询和left join查询拆开,先select count查询总数,然后再查询当页数据
7.8.1.内置分页
优化left join count场景
当对这条sql查询结果进行分页查询,执行的sql语句是这样的
加入count语句解析器后进行分页查询,最终执行的sql语句是
先配置分页 生成PaginationInterceptor分页拦截器
测试
7.8.2.自定义xml分页
生成分页拦截器
UserMapper.xml文件
UserMapper接口
application.yml
测试
7.8.3.pageHelper插件分页
接口方法
测试