SpringBoot为我们提供了一个方便我们开发测试的工具dev-tools。使用后可以实现热部署的效果。当我们运行了程序后对程序进行了修改,程序会自动重启。
- 原理是使用了两个 ClassLoder ,一个 ClassLoader 加载那些不会改变的类(第三方jar包),另一个 ClassLoader 加载会更改的类,称之为 Restart ClassLoader 。
- 这样在有代码更改的时候,原来的 Restart Classloader 被丢弃,重新创建一个 Restart ClassLoader,由于需要加载的类相比较少,所以实现了较快的重启。
- 设置IDEA自动编译
-
设置允许程序运行时自动启动
ctrl + shift + alt + / 这组快捷键后会有一个小弹窗,点击Registry 就会进入下面的界面
或者 ctrl + shift + A 弹出搜索框,然后搜索 Registry
- 添加依赖
org.springframework.boot spring-boot-devtools true
-
触发热部署
当我们在修改完代码或者静态资源后可以切换到其它软件,让IDEA自动进行编译,自动编译后就会触发
热部署。或者使用 Ctrl+F9 手动触发重新编译。(Build -> Build Project)
2、单元测试我们平常学习可以使用这个热部署工具,如果在项目中还是推荐不使用
在实际开发中,每当完成一个功能接口或业务方法后,通常都会借助单元测试验证该功能是否正确,Spring Boot 对项目的单元测试提供了良好的支持,在使用时,需要提前在项目的 pom.xml 文件中添加 spring-boot-starter-test 测试依赖启动器,可以通过相关注解实现单元测试。
我们可以使用SpringBoot整合Junit进行单元测试。
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库。
2.1、使用步骤- 添加依赖
org.springframework.boot spring-boot-starter-test
需要注意的是,如果是使用 Spring Initializr 方式搭建的 Spring Boot 项目,会自动加入 spring-boot-starter-test 测试依赖启动器,无需开发者再次手动添加。
- 编写测试类
@SpringBootTest // 标记该类是一个 Spring Boot d
public class ApplicationTest {
@Test
public void testJunit(){
System.out.println(1);
}
}
注意:测试类所在的包需要和启动类是在同一个包下。
如果不在同一个包下,那么就要使用如下写法指定启动类
//classes属性来指定启动类
@SpringBootTest(classes = HelloApplication.class)
public class ApplicationTest {
@Test
public void testJunit(){
System.out.println(1);
}
}
2.2、兼容老版本
如果是对 SpringBoot 2.2.0 之前的项目进行版本升级就会发现之前的单元测试代码出现一些问题,这是因为 2.2.0 之前使用的是 junit4 ,而 2.2.0 之后使用的是 junit5 。
我们来看一下 SpringBoot 2.2.0 之前单元测试的写法
@SpringBootTest
@RunWith(SpringRunner.class)
public class ApplicationTest {
@Test
public void testJunit(){
System.out.println(1);
}
}
junit4 版本,需要搭配 @RunWith 注解来使用。
所以如果我们在依赖中对 SpringBoot 升级,就会出现单元测试代码爆红的问题。如何解决这个问题呢?我们先来看一张图
从上图可以看出 JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
- JUnit Platform: 这是Junit提供的平台功能模块,通过它,其它的测试引擎也可以接入
- JUnit JUpiter:这是JUnit5的核心,是一个基于JUnit Platform的引擎实现,它包含许多丰富的新特性来使得自动化测试更加方便和强大。
- JUnit Vintage:这个模块是兼容JUnit3、JUnit4版本的测试引擎,使得旧版本的自动化测试也可以在JUnit5下正常运行。
虽然 Junit5 包含了 JUnit Vintage 来兼容 JUnit3 和 Junit4 ,但是 SpringBoot 2.4 以上版本对应的 springboot-starter-test 移除了默认对 Vintage 的依赖。所以当我们仅仅依赖 spring-boot-starter-test 时会发现之前我们使用的 @Test 注解和@RunWith 注解都不能使用了。
解决办法就是我们单独依赖 vintage 来进行兼容
3、整合mybatisorg.junit.vintage junit-vintage-engine test
sql 语句
DROp TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) DEFAULT NULL, `age` INT(11) DEFAULT NULL, `address` VARCHAR(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; INSERT INTO `user`(`id`,`username`,`age`,`address`) VALUES (2,'pdd',25,'上海'), (3,'UZI',19,'上海11'),(4,'RF',19,NULL),(6,'三更',14,'请问2'),(8,'test1',11,'cc'), (9,'test2',12,'cc2');
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private Integer age;
private String address;
}
3.1、整合步骤
- 添加依赖
org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.0 mysql mysql-connector-java runtime
这里的 mybatis 启动器的版本 springboot 并没有指定,我们需要自己设置版本号,如何查看自己应该引入什么版本呢?
github: https://github.com/mybatis/spring-boot-starter/
- 配置数据库信息
spring: datasource: url: jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
properties 写法如下:
#加载驱动 spring.datasource.driver-class-name=com.mysql.jdbc.Driver #数据库连接路径 spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai #数据库用户名 spring.datasource.username=root #数据库密码 spring.datasource.password=123456
数据源类型选择配置,如果需要使用其他数据源,还需要进行额外配置,这里选择阿里巴巴的 Druid 数据源,需要在 pom.xml 配置文件中添加 Druid 数据源的依赖启动器,代码如下所示:
com.alibaba druid-spring-boot-starter 1.2.1
上述引入的依赖 druid-spring-boot-starter,同样是阿里巴巴为了迎合 Spring boot 项目而适配的 Druid 数据源启动器,当在 pom.xml 文件中引入该启动器后,不需要进行其他额外配置,Spring boot 项目会自动识别配置 Druid 数据源。
需要注意的是,上述配置的 Druid 数据源启动器内部已经初始化了一些运行参数(例如:initialSize,maxActive等),如果开发过程中需要修改第三方 Druid 的运行参数,则必须在全局配置文件中继修改,代码如下所示:
#添加并配置第三方数据源 #1.设置数据源类型 spring.databsource.type = com.alibaba.druid.pool.DruidDataSource #2.设置初始化连接数 spring.databsource.druid.initial-size = 20 #3.设置最小空闲数量 spring.databsource.druid.max-active = 100 #4.设置最大连接量 spring.databsource.druid.max-active = 100
-
配置 mybatis 相关配置
在项目中编写的 XML 映射文件,Spring Boot并无从知晓,所以无法扫描到该自定义编写的 XML 映射文件,需要在全局配置文件 application.yml / application.properties 中添加 Mybatis映射文件的路径,同时也可以添加实体类的别名设置。
- 这是yaml写法
mybatis: mapper-locations: classpath:mapper/*Mapper.xml # mapper映射文件路径 type-aliases-package: com.sangeng.domain # 配置哪个包下的类有默认的别名
- 这是 properties 写法
#mybatis取别名 mybatis.type-aliases-package=com.sangeng.domain #扫描映射文件 mybatis.mapper-locations=classpath*:mapper INSERT INTO `user`(`id`,`username`,`age`,`address`) VALUES (2,'pdd',25,'上海'), (3,'UZI',19,'上海11'),(4,'RF',19,NULL),(6,'三更',14,'请问2'),(8,'test1',11,'cc'), (9,'test2',12,'cc2');
application.yml 中配置如下
server:
port: 81
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml # mapper映射文件路径
type-aliases-package: com.sangeng.domain # 配置哪个包下的类有默认的别名
实体类如下
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Integer age;
private String address;
}
- 首先写 dao 层接口,并用插件生成对应的 Mapper.xml 文件
@Mapper
@Repository
public interface UserMapper {
List findAll();
}
- 接着写 service 层接口和其实现类,并在实现类中注入 dao
@Service
public interface UserService {
List findAll();
}
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List findAll() {
return userMapper.findAll();
}
}
- 最后编写 controller 层,并在 controller 层中注入 service
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
public List findAll(){
// 调用 service 查询数据,进行返回
return userService.findAll();
}
}
- 启动测试
我们查到的结果是 List 集合,List 集合会转换为 json 放入到响应体当中,但是我们如果是根据 id 查询,那么只会查出一个用户,是对象格式。我们要保证一个项目中所有接口返回的数据格式的统一。这样无论是前端还是移动端开发获取到我们的数据后都能更方便的进行统一处理。我们这里是将所有的数据格式统一为 json 格式。
所以我们定义一下结果封装类 ResponseResult,由于格式统一是公用的,所以我们可以在 com.sangeng 下创建一个公共 common 包,在包下创建结果封装类 ResponseResult
// 加上此注解,如果某个属性的值不为NULL,才会将其转化为Json格式 @JsonInclude(JsonInclude.Include.NON_NULL) public class ResponseResult{ private Integer code; private String msg; private T data; // 三个参数的有参构造(查询操作需要返回查询的数据data) public ResponseResult(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } // 两个参数的有参构造(状态码和提示信息) public ResponseResult(Integer code, String msg) { this.code = code; this.msg = msg; } // 两个参数的有参构造(状态码和数据) public ResponseResult(Integer code, T data) { this.code = code; this.data = 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; } }
我们修改我们的查询用户 controller 层代码,让其返回的是我们封装好的 ResponseResult 格式,而不是 List 集合
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
public ResponseResult findAll(){
// 调用 service 查询数据,进行返回
List users = userService.findAll();
return new ResponseResult(200,users);
}
}
再次启动测试
4.3、跨域请求-
什么是跨域?
浏览器出于安全的考虑,使用 XMLHttpRequest 对象发起 HTTP 请求时必须遵守同源策略,否则就是跨域的 HTTP 请求,默认情况下是被禁止的。同源策略要求源相同才能进行正常通信,即协议、域名、端口号都完全一样。
参考博客:Ajax前后端交互利器详解(二)
例如我们想要渲染一个表格里面的数据,前端请求后端的数据,然后将后端传输过来的数据渲染到前端表格页面上。
- 前端页面中我们使用 axios 发起请求,请求后台接口,前端页面请求代码如下:
那我们如何解决上述问题呢?
-
CORS 解决跨域
CORS 是一个 W3C 标准,全称是 “跨域资源共享”,允许浏览器向跨源服务器发出 XMLHttpRequest 请求,从而克服了 Ajax 只能同源使用的限制。
它通过服务器增加一个特殊的 Header[Access-Control-Allow-Origin] 来告诉客户端跨域的限制,如果浏览器支持 CORS,并且判断 Origin 通过的话,就会允许 XMLHttpRequest 发起跨域请求。
在Web开发中,经常会遇到跨域开发的问题,常用的跨域解决方案有 JSNOP、Iframe、CORS等。CORS(Cross-OriginResource Sharing 跨域资源共享),当一个请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
CORS(Cross-OriginResource Sharing 跨域资源共享),是一个W3C标准,它允许浏览器向跨域服务器发生Ajax请求,打破了Ajax只能访问本站内的资源限制。
在Spring4.2开始,SpringMVC支持开箱即用的CORS。在SpringBoot应用程序中使用带有 @CrossOrigin 注解,该注解可标记在控制器的类上或具体的某个方法上实现跨域请求,也可以通过自定义方法注册Bean来定义全局的CORS配置。
4.4.1、加注解@CrossOrigin-
使用 @CrossOrigin ,给 controller 层的方法加上此注解
这个注解可以加在类上,也可以加在方法上
- 加在类上,指明这个类中的所有方法都可以进行跨域请求
- 加在方法上,指明这个方法可以进行跨域请求
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
@CrossOrigin
public ResponseResult findAll(){
// 调用 service 查询数据,进行返回
List users = userService.findAll();
return new ResponseResult(200,users);
}
}
这样我们再次使用前端页面请求后台接口,发现请求成功
4.4.2、使用WebMvcConfigurer的addCorsMappings方法配置CorsInterceptor上述方法使用注解加在类上或者方法上虽然看起来方便,但是如果我们有很多的 controller 方法,那么就需要加很多个注解,所以其实并不方便。
- 我们创建一个包,包下创建类,让类实现 WebMvcConfigurer 接口,并重写 addCorsMappings 方法,如下:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许cookie
.allowCredentials(true)
// 设置允许的请求方式
.allowedMethods("GET","POST","DELETE","PUT")
// 跨域允许时间
.maxAge(3600);
}
}
这样我们再次使用前端页面请求后台接口,发现请求成功。
这样我们就解决了跨域请求,我们就可以真正让前面页面请求后台数据来渲染我们的表格数据了.
我们先来看请求成功之后箭头函数返回的 res 对象里面的内容,我们可以在浏览器中打断点调试
当然我们也可以在控制台打印 res 对象
这样我们就可以拿到前端页面请求返回的后台数据对象 res,而 res 里面又有我们的数据,我们可以通过遍历来拿到数据,然后放在前端页面表格里面
学号 姓名 年龄 地址 {{user.id}} {{user.username}} {{user.age}} {{user.address}}
这就是前后端分离式开发。



