SpringBoot
基础
介绍
Spring家族有一套完全,之前学习的Spring5(Spring Framework),SpringMVC,都是Spring家族的组件,用于解决某一问题的,可以这么说
Spring家族还有很多的组件,在以前,整合起来会非常的麻烦,要写很多的配置文件,有了SpringBoot就会方便去整合,有了它,可以快速去构建出生产级别的Spring应用
背景
微服务
- 微服务是一种架构风格
- 一个应用拆分成一组小型服务
- 每个服务运行在自己的进程里,可独立部署和升级
- 服务之间使用轻量级HTTP进行交互
- 服务围绕业务功能进行拆分
- 可以由全自动部署机制独立部署
- 去中心化,服务自治。服务可以使用不同的语言,不同的存储技术
分布式
分布式-有了微服务的架构,就必然会有分布式的概念
分布式的难点:
- 远程调用
- 服务发现
- 负载均衡
- 服务容错
- 配置管理
- 服务监控
- 链路追踪
- 日志管理
- 任务调度
- …
分布式难点的解决:
- SpringBoot+SpringCloud
云原生
应用写出来就会有上云的问题,上云的过程中又会出现很多问题
难点:
- 服务自愈
- 弹性伸缩
- 服务隔离
- 自动化部署
- 灰度发布
- 流量自理
- …
解决:
- 云原生
优点
- 创建独立的Spring应用
- 内嵌web服务器
- 自动starter依赖,简化构建配置
- 自动配置Spring以及第三方功能
- 提供生产级别的监控,健康检查以及外部化配置
- 无代码生成,无需编写XML
- 对于很多的东西(拿不到源码的Java文件),不需要再去写配置Bean,直接在配置文件就可以解决
缺点
迭代快,需要时刻关注变化
封装太深,内部原理复杂,难以精通
使用
多看文档。
https://docs.spring.io/spring-boot/docs/current/reference/html/
初使用
Maven版
父级依赖
- 这个项目是父项目,用于依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
</parent>
引入场景依赖
- 这个引入的是SpringBoot的依赖
- 这个依赖会把与这个场景相关的依赖全部引入进来,不需要我们自己手动去找依赖!
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
编写启动程序
- SpringBoot内置了有web服务器,所以说我们不用手动配置Tomcat服务器,直接写个程序启动就行了如下操作
@SpringBootApplication
标识这个类是主程序类,用它去启动SpringBoot程序- 这个类里main方法就是用来启动的,写法如下
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
编写配置文件
- 在resource目录里(资源目录)新建一个
application.properties
,这一个配置文件可以将web服务器等配置进行修改,具体看官方文档 - 如果没需求,这个文件可以不写
- 下面demo就是修改服务访问端口的配置
server.port=8888
编写服务
- 服务就是控制层
@RestController
是@Controller
和@ResponseBody
的复合注解,标识这是控制层,同时标识这个类下的方法都会给页面一个响应- 有
@ResponseBody
这个注解,方法返回的字符串就会直接响应在浏览器内容中
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01() {
return "Hello,SpringBoot2";
}
}
简化部署
何为简化部署?
- 就是我们不用手动去打jar包(不加配置打出来的jar包是不能够直接运行的)
- SpringBoot为我们提供了maven插件,可以让我们直接打成jar包,然后在终端中直接运行
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动服务
- 启动服务就是将启动程序启动,启动后在浏览器里输入
localhost:8888/hello
,就会出现Hello,SpringBoot2
的页面。 - 启动服务可以在IDEA里面启动,也可以在终端中运行jar包
特点
依赖管理
修改依赖
初使用里面的pom文件里面有个parent标签,那是SpringBoot帮我们在做依赖管理,我们可以依次往上翻,找到真正的父级项目依赖管理
在
spring-boot-dependencies.pom
文件里可以看到SpringBoot帮我们把会用到的依赖都引入了,还有默认版本号如果我们需要自定义依赖版本号,那么就在自己项目的父类项目里按一下方式写
- 具体细节翻看Maven
<properties>
<mysql.verson>8.0.23</mysql.verson>
<properties>
修改场景启动器
- 初使用项目中,我们看到只引入了
spring-boot-starter-web
依赖,这个就叫做场景启动器,就是web的依赖,会将web需要的依赖全部导入。 - 具体翻源码
spring-boot-starter-*
*-spring-boot-starter
类似这种的就是第三方的场景启动器
自动配置
自动配好Tomcat服务器
自动配好SpringMVC常用组件
自动配好Web常用功能
- 字符编码
- 文件上传
- …
默认包结构
- 主程序所在的包以及其下面所有子包里的所有被默认扫描
各个配置文件拥有默认值
application.properties
文件中的默认配置都会绑定一个类,这个类会在容器中创建对象
按需加载所有自动配置项
- 自动配置的依赖在场景启动器的依赖中,名为
spring-boot-autoconfigur.jar
- 这个jar包里有很多的自动配置项,有缓存,aop等等
- 但是SpringBoot会根据你引入的 场景启动器去开启需要的,不需要的则会爆红(具体如何实现的后续会讲)
- 自动配置的依赖在场景启动器的依赖中,名为
容器功能
组件添加
@Configuration
- 在SpringBoot中,我们要为容器添加bean组件,可以使用纯XML的方式,也可以写配置类,在配置类里引入bean
@Configuration
标识在类上,告诉SpringBoot这是一个配置类- 在配置类里对方法使用
@Bean
注解,这样就可以为容器注册bean组件 - 这个方法也是有讲究的,看下面的注释
- 详情翻阅Spring笔记
@Configuration
public class MyConfig {
/**
*给容器注册组件
*方法名作为组件id
*返回类型就是组件类型
*/
@Bean
public User user01(){
return new User("zhangsan", 18);
}
}
full-lite
- 上面的操作注册的组件(bean)是单例模式的,就是单实例对象。
- 但我们有时候不需要单实例模式,这样就只需要下面的操作
- 默认是true,会创建动态代理
@Configuration(proxyBeanMethods = false)
public class MyConfig {
@Bean
public User user01(){
return new User("zhangsan", 18);
}
}
@ComponentScan @Import
- 在容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
- 这种使用方式下就不用
- 用在
@Configuration
注解下面
@Import({User.class, DBHelper.class})
@Conditional
@Conditional
及其子注解是用于条件注册组件的
XML配置文件引入
- 我们有时候会遇到写了很多的配置文件,想迁移到配置类里里面,但是太繁琐,这个时候我们就在配置类哪里使用注解将
beans.xml
引入 - 使用下面这个注解,就会把XML文件中配置的bean组件注册到容器中
@ImportResource("classpath:beans.xml")
配置绑定(Properties)
- 配置绑定就是将配置文件里面的信息注入到某个类里
- 以下两种实现方式
/**
*方案一:
*perfix就相当于是这个类的别名
*然后就可以在SpringBoot配置文件里写了
*/
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
/**
*方案二:
*在配置类上面使用注解@EnableConfigurationProperties(类名.class)
*只在Car类上使用@ConfigurationProperties(prefix = "mycar")
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(Car.class)
public class MyConfig {
}
mycar.brand=BYD
mycar.price=100000
开发小技巧
Lombok
- Lombok好用,帮我们简化开发,我们的pojo类,可以只写字段,而使用Lombok提供的注解简化
- 使用注解后就可以不写构造器,set/get方法,toString方法等等,Lombok会在程序编译的时候为我们补上
- 记得导包和安装IDEA的lombok插件
- 注解不止下面几种,还有别的,比如
@Slf4j
,用了这个我们就不用在主程序去打印了,直接在类哪里使用log.info()
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private int price;
}
Spring Initailizer
前面哪个项目是使用maven手动去设置的SpringBoot项目,我们可以继续简化开发,使用Spring的初始化程序去创建项目
dev-tools
相当于就是热部署,想用速度快些,添加依赖,Command+F9
配置文件
SpringBoot开发的时候,配置文件有两种
一种是:properties
二种是:yaml
yaml
yaml是一种标记语言,类似markdown,html等,非常适合用来做以数据为中心的配置文件
基本语法
- key:value,kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系,缩进不允许使用tab,只允许空格
- 缩进的空格数不做要求,对齐即可
- #表示注释
- ‘’和”” 表示的字符串内容 会被 转义/不转义
数据类型
字面量
- 字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
对象
- 字面量:单个的、不可再分的值。date、boolean、string、number、null
行内写法: k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
数组
- 数组:一组按次序排列的值。array、list、queue
行内写法: k: [v1,v2,v3]
#或者
k:
- v1
- v2
- v3
示例
需要注意的是List就是数组
@ConfigurationProperties(prefix = "person")
@Component
@ToString
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
@Component
@ToString
@Data
public class Pet {
private String name;
private Double weight;
}
person:
user-name: "Jsckot"
boss: true
birth: 2001/11/28 20:12:33
age: 20
pet:
name: "tomcat"
weight: 23.4
interests: ["篮球","游泳"]
animal:
- Jerry
- Mario
score:
english:
first: 20
second: 40
third: 50
math: {23,45,65}
chinese: {first: 34,second: 43,third: 65}
salarys: [1234,6789,9877,7777]
all-pets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: Mario,weight: 47.9}]
yml简化开发
我们在使用yml以及properties两种文件类型做配置绑定的时候,IDEA不会提醒我们去写类的字段,这时候我们可以使用SpringBoot提供的依赖去辅助我们开发
tip:这个依赖只是用于方便我们的开发,项目运行并不需要这个,所以我们可以将这个依赖在编译的时候移除是,以免JVM需要加载过多无用的类
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
WEB开发
WEB开发中,SpringBoot为我们做了很多的自动配置,具体翻看文档
简单功能
静态资源访问
- 类路径下的
/static
or/publuc
or/resources
or/META-INF/resources
- 只要文件在类的这些目录下,项目启动后就可以通过
域名:端口/静态资源名
进行访问 - 原理:静态映射
/**
- 请求进来先看Controller能否处理,不能处理的则又交给静态资源处理器,就会去指定目录寻找,找不到就404
静态资源访问前缀
- 这个功能挺有用的,上面的操作是默认的,没有前缀,直接使用
域名:端口/静态资源名
- 我们可以进行操作,使其可以按这种路径访问
域名:端口/访问前缀/静态资源名
spring:
mvc:
static-path-pattern: /res/**
自定义资源路径
自定义静态资源路径可以使用自定义的,默认是关闭的,开启后上面的默认静态资源就没用了,就完全按照自定义的路径来
开启后就是按照以下的操作来
- 将静态资源文件放到
/haha
目录下,然后按照自己设定的访问前缀进行访问 - 这个路径是一个数组,可以自定义多个资源路径
- 将静态资源文件放到
spring:
web:
resources:
static-locations: [classpath:/haha/]
webjars
- webjars就是就是,比如我们常用的BootStrap,JQuery等等,SpringBoot帮我们封装在了jar包里[
jar包其实就是一个压缩包
],然后使用具体路径去访问 - http://localhost:8080/webjars/jquery/3.5.1/jquery.js
- 寻找对应的jar包就去这个网站:https://www.webjars.org/
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
欢迎页
index.html
文件放到静态资源目录下,启动程序的时候就会首先访问这个- 可以配置自定义静态资源路径
- 但是不可以配置静态资源的访问前缀
controller也能够处理 /index,都会被当成静态页
自定义Favicon
- Faviocn就是网站在浏览器中访问的时候,浏览器上面的小图
- 直接将文件改为
favicon.ico
,然后放在静态资源目录下即可
请求处理
请求映射
以前:
- /getUser 获取用户
- /deleteUser 删除用户
- /editUser 修改用户
- /saveUser 保存用户
现在:
- /user GET-获取用户
- /user DELETE-删除用户
- /user PUT-修改用户
- /user POST-保存用户
使用:
- 先在SpringBoot配置文件中开启隐藏method
- 表单中使用POST请求
- 前端使用隐藏域,name属性为_method
- 请求映射哪里使用
method = RequestMethod.GET
指定
原理:
- 表单提交会带上**_method=PUT**
- 请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到**_method**的值。
- 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
注意:这个主要是用于页面开发,所以在表单请求的时候才会有效,因为表单只能够有post和get
- 如果直接http请求,就会直接变成对应的请求方式
使用PostMan等测试工具直接发送DELETE,就会和上一行说得一样,直接会变成DELETE,而不会经过filter
spring:
mvc:
hiddenmethod:
filter:
enabled: true
@RestController
public class HelloController {
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
}
/**上面的操作**/
@RestController
public class HelloController {
@GetMapping("/user")
public String getUser(){
return "GET-张三";
}
@PostMapping("/user")
public String saveUser(){
return "POST-张三";
}
@PutMapping("/user")
public String putUser(){
return "PUT-张三";
}
@DeleteMapping("/user")
public String deleteUser(){
return "DELETE-张三";
}
}
改变默认_method
- 修改后记得去前端进行连调修改
package com.Jsckot.thirdweb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
}
普通参数与基本注解
- SpringBoot的web层是基于SpringMVC实现的,在Controller的请求方法的参数中,可以传很多的参数来获取信息
- 下面介绍的就是一些常用的信息
基本注解
@PathVariable
–路径变量–就是请求路径里的变量 -user/{id}
@RequestHeader
–获取请求头信息@RequestParam
–获取请求参数–就是?携带的信息
@CookieValue
–获取cookies@RequestBody
–获取请求体信息[post请求的数据]@RequestAttribute
–获取request域信息@MatrixVariable
–矩阵变量获取的信息都是健值对形式的,可以使用上面的注解将全部的信息或者某一个key的信息获取
获取全部就是将获取的信息放在Map中,健值对嘛
获取特定的信息就是 将这个key放注解的参数里传给具体的变量
@RestController
public class ParameterTestController {
@GetMapping("/car/{id}/owner/{username}")
public Map<String,Object> getCar(@PathVariable("id") Integer id,
@PathVariable("username") String name,
@PathVariable Map<String,Object> pv,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String,String> headers) {
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("pv",pv);
map.put("User-Agent",userAgent);
map.put("header",headers);
return map;
}
}
ServletAPI
- 参数也可以传ServletAPI参数,比如HttpServlet等等
复杂参数
- 就是传存map,也都是request域
- 键值对形
自定义对象参数-POJO
SpringMVC支持将前端的数据模型封装在Java的类里面,然后前端的数据发起请求后可以通过请求方法直接封装在POJO类对象供后端进行操作
- 支持级联属性,比如下面5,6行的代码
<form action="/saveuser" method="post">
姓名: <input name="userName" value="zhangsan"/> <br/>
年龄: <input name="age" value="18"/> <br/>
生日: <input name="birth" value="2019/12/10"/> <br/>
<!-- 宠物姓名:<input name="pet.name" value="阿猫"/><br/>-->
<!-- 宠物年龄:<input name="pet.age" value="5"/>-->
宠物: <input name="pet" value="啊猫,3"/>
<input type="submit" value="保存"/>
</form>
// 下面的代码可以不写,如果需要自定义handle再来写
@Bean
public WebMvcConfigurer webMvcConfigurer() {
// 这是Lamada表达式的写法
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
if (!StringUtils.isEmpty(source)) {
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(split[1 ]);
}
return null;
}
});
}
};
}
响应处理
响应JSON
准备工作
- web场景依赖+jackson.jar
- 映射方法上标注
@ResponseBody
注解 - 返回的就是JSON类型
上面的测试中,返回的都是XML
响应XML
- 将jackson-dataformat-xml.jar的依赖导入进来
- 映射方法上标注
@ResponseBody
注解
内容协商
内容协商就是使用会根据请求头信息获取返回的类型,是json还是xml
只不过需要把xml和json对应的依赖导入
如果要返回xml,就把请求头的
accept
改为application/xml
这样的话就会返回xml格式的数据
- 也可以使用携带参数来实现,开启这个参数就行了
- 就像这样
http://localhost:8080/saveuser?format=xml
// 开启内容协商
spring:
contentnegotiation:
favor-parameter: true
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
自定义内容协商处理
- 可以使用WebMvcConfig去定制化SpringMVC功能,需要的时候自己去看文档
视图解析与模板引擎
视图解析:SpringBoot不再支持JSP,需要引入第三方模板引擎技术去实现页面渲染与跳转
常用Thymeleaf
Thymeleaf使用
- 导入Thymeleaf的依赖-SpringBoot会为我们配置好Thymeleaf以及模板引擎,视图解析器
- 在html页面中引入Thymeleaf的名称空间-可以直接修改IDEA的配置信息
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
FreeMaker
也是一种模板引擎,可以了解一下
解决表单重复提交
- 在使用请求映射的情况下会出现表单重复提交样子,比如这个demo
- 因为它是映射的
main.html
这个文件,如果已经映射了再刷新就会再次发起请求
@PostMapping("/login")
public String main(String username,String passward) {
return "main";
}
- 解决方案如下:
- 访问/login这个请求映射的时候,满足情况的话会带着请求参数直接重定向到具体的页面,而不是请求本身。
- 这个点去了解一下请求与重定向等等
@PostMapping("/login")
public String main(String username,String passward) {
//登录成功重定向到main.html
return "redirect:/main.html";
}
@GetMapping("/main.html")
public String mainPage() {
return "main";
}
抽取公共部分
- 将公共部分抽取后放到一个html页面中,然后使用
th:formtarget
命名,再在原页面中引入即可
拦截器
- 拦截器和原生Servlet里面的 Filter功能类似,都是用于拦截的
HandlerInterceptor接口
HandlerInterceptor接口
是拦截器接口,有三个默认方法,用于实现拦截。
操作
- 新建一个类实现拦截器接口,实现拦截的方法
- 将拦截器类注入到容器中,在容器中配置拦截以及放行的路径
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/","/login","/css/**","fonts/**","/images/**","/js/**","/aa/**");
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
文件上传
- SpringBoot将文件上传封装到了Multipart里面了,原理后续专门看,很方便
操作
前端写好文件上传的表单
- 请求方法使用post
- action使用thymeleaf语法,提交给请求映射
- enctype设置为
multipart/form-data
后端将请求映射的方法写好,将文件写入目标目录,具体看demo
修改SpringBoot配置文件,因为默认上传大小只有1MB
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
/**
*使用注解将前端上传的文件标识
*/
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws Exception{
log.info("{}---{}---{}---{}---{}",email,username,headerImg.getSize(),photos.length);
if (!headerImg.isEmpty()) {
//获取原文件名
String originalFilename = headerImg.getOriginalFilename();
//使用Multipart的transferTo方法将文件写入目标目录
headerImg.transferTo(new File("/Users/Jsckot/Desktop/upload/"+originalFilename));
}
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("/Users/Jsckot/Desktop/upload//"+originalFilename));
}
}
}
return "redirect:/main.html";
}
异常处理(TODO)
默认情况下SpringBoot提供/error
处理所有请求的映射
- 对于机器客户端,它将响应json
- 对于浏览器,会响应一个页面
{
"timestamp": "2022-02-18T08:35:42.807+00:00",
"status": 999,
"error": "None",
"message": "No message available"
}
自定义页面处理
- 自定义处理就是可以在resource/templates目录下创建一个error目录,里面放对应的页面,SpringBoot会帮我们自动解析,当遇到访问为5xx或者404的时候就会自动访问页面
- 我们也可以通过json将报错栈放到页面上
- 5xx表示响应状态码为5开头就会自定响应这个页面
<section class="error-wrapper text-center">
<h1><img alt="" src="images/500-error.png"></h1>
<h2>OOOPS!!!</h2>
<h3 th:text="${message}">Something went wrong.</h3>
<p class="nrml-txt" th:text="${trace}">Why not try refreshing you page? Or you can <a href="#">contact our support</a> if the problem persists.</p>
<a class="back-btn" th:href="@{/main.html}"> Back To Home</a>
</section>
定制异常处理
处理全局异常
@ControllerAdvice
和@ExceptionHandler
注解配合使用进行全局异常捕获@ControllerAdvice
继承了@Component
@ExceptionHandler
注解用于自定全局异常类型- 返回的是视图地址
- 这个操作是全局的,全局内遇到后会自动进行响应
/**
* 处理整个web controller异常
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理异常
* @return
*/
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public String handlerArithException(Exception e) {
return "login"; //返回一个视图地址
}
}
@ResponseStatus+自定义异常
- 自定义异常,然后再在程序中抛出这个异常
- reason属性会被封装在message里面
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "用户数量太多")
public class UserTooManyException extends RuntimeException{
public UserTooManyException() {
}
public UserTooManyException(String message) {
super(message);
}
}
自定义异常解析器
- 这是自定义异常解析器,用的不多,配合Order注解可以实现优先级的分配
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
try {
response.sendError(511,"我喜欢的错误");
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
原生组件
- 原生组件比如Servlet,Filter,Listener等等,注入容器有两种方式
原生注解➕包扫描
- 原生注解就是
@WebServlet
,再在启动器上面加一个Servlet的包扫描注解 - 这样的话就可以通过
/myservlet
路径进行访问,这个访问是不受Spring拦截器的拦截的,直接访问
@Slf4j
@WebServlet(urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("11111");
resp.getWriter().write("666666");
}
}
@ServletComponentScan(basePackages = "com.Jsckot.webtest.servlet")
@SpringBootApplication
public class ForthTestApplication {
RegistrationBean注解
- 使用RegistBean的话,就必须先写好上面的MyServlet的自定义类
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet() {
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet);
}
}
定制Servlet容器
数据访问
- 数据访问就和数据库挂钩了。
数据源的自动配置
JDBC场景
- 导入JDBC依赖
- 导入数据库驱动-因为SpringBoot也不知道我们需要使用哪个-而且也有版本仲裁
- 配置文件写好数据源信息
- 再使用JdbcTemplate进行单元测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssmbuild?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
username: root
password: xxxxxxxxx
driver-class-name: com.mysql.cj.jdbc.Driver
使用Druid数据库连接池
JDBC依赖是是使用的Hikari的数据库连接,现在整合Druid
引入三方的技术,有两种方式
- 自定义-就是将依赖引入,一项项的去写配置
- 找对应的starter
自定义方式
导入Druid的依赖
新建一个配置类
配置连接信息
- 连接信息的话和上面一样,写在SpringBoot的配置文件中
- 将配置信息导入到数据库连接池的配置信息中
配置Druid监控页面等-根据官方文档
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssmbuild?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
username: root
password: xxxxxxx
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat,wall
@Configuration
public class MyDataSourceConfig {
//配置连接池信息
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource ;
}
/**
* 配置监控页
* @return
*/
@Bean
public ServletRegistrationBean statViewServlet() {
//配置监控页路径
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet,"/druid/*");
//配置监控页访问账号密码
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123456");
return registrationBean;
}
/**
* WebStatFilter -用于采集Web-Jdbc关联监控的属性
* 拦截排除的连接,将拦截到的展示在监控平台
* @return
*/
@Bean
public FilterRegistrationBean WebStatFilter() {
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> registrationBean = new FilterRegistrationBean<>(webStatFilter);
registrationBean.setUrlPatterns(Arrays.asList("/*"));
registrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return registrationBean;
}
}
starter方式
- 引入starter-spring-boot-druid
- SpringBoot配置文件中写配置
- SpringBoot会为我们自动配置,不用手写AutoConfig
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssmbuild?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
username: root
password: xxxxxxs
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
aop-patterns: com.Jsckot.webtest.* #监控SpringBean
filters: stat,wall #sql监控和防火墙
stat-view-servlet:
enabled: true #开启监控页
login-username: admin #监控账号
login-password: 123456 #监控密码
reset-enable: false #不允许重置
web-stat-filter:
enabled: true #开启url拦截
url-pattern: /* #拦截的请求
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' #不拦截的请求
filter:
stat: 对上面filters里面的进行详细配置
slow-sql-millis: 1000
log-slow-sql: true
enabled: true
wall:
enabled: true
config:
delete-allow: false #不允许删表
整合MyBatis
- 引入mybatis-spring-boot-starter
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot</artifactId>
<version>2.2.3-SNAPSHOT</version>
</dependency>
配置方式
全局配置文件-自己写好,放在
/resource/mybatis
SqlSessionFactory:【SpringBoot已自动配好,不用再手动进行配置bean操作】
SqlSession:【自动配置了SqlSessionTemplate,里面自动组合了SqlSession】
Mapper:只要我们写的Mybatis的接口标注了
@Mapper
注解就会被自动扫描进来超级方便,再自己写好接口和映射文件即可,可以搭配IDEA插件
MyBatisX
进行使用操作
- 导入mybatis的starter
- 编写mapper接口–(一定要标注
@Mapper
注解) - 编写sql映射文件并绑定mapper接口
- 在
application.yml
中指xml全局配置文件的位置和mapper映射文件
mybatis-config.xml
全局配置文件可以不写,直接使用mybatis-configuration
(application.yml)进行配置
#配置MyBatis的规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件,在配置文件里写全局配置
mapper-locations: classpath:mybatis/mapper/*.xml #mapper映射文件
注解方式
- 注解模式就是使用注解,不写mapper映射文件,不过只适合简单的sql语句
混合模式
- 混合模式就是将配置方式和注解方式 混合使用,简单的sql语句就使用注解,复杂的sql语句就使用配置方式
整合MyBatis-Plus
引入MyBatis-Plus的starter-【可以不用引入MyBatis的】
- MyBatis-Plus的mapper映射文件目录,可以不写,因为有默认值
classpath:/mapper/**/*.xml
,以后我们就把mapper映射文件放到这里
- MyBatis-Plus的mapper映射文件目录,可以不写,因为有默认值
写Mapper接口-将接口继承
BaseMapper类
,这样的话就能满足基本的CRUD操作MyBatis-Plus的注解也可以完成很多的步骤!!比如TableField
…学习MyBatisPlus后再来复习
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
单元测试
- SpringBoot2.2.0版本开始引入JUnit5作为单元测试默认库
- 和以前的有很大不同
- SpringBoot2.4及以上移除了JUnit4
准备工作
- 引入JUnit的starter依赖
- 使用注解
@Test
,jupiter包下的 - 编写方法
- JUnit具有Spring的功能
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
常见注解
@DisplayName注解
@DisplayName("JUnit5功能测试1")
public class Junit5Test {
@DisplayName("测试displayname注解")
@Test
void testDisplayName() {
System.out.println(1);
}
}
@BeforeEach注解
这个注解是用于提示的,每个测试开始前都会提示这句话
@BeforeEach
void testBeforeEach() {
System.out.println("测试即将开始...");
}
@AfterEach注解
和上面的一致
@AfterEach
void testAfterEach() {
System.out.println("测试已经结束...");
}
···
断言机制
断言是测试方法中的核心部分,用来对测试需要满足的条件进行验证
分为以下几个品类
- 检查业务逻辑返回的数据是否合理
- 所有测试运行结束以后会有一个详细的测试报告
前置条件
和断言类似,可以说是假设。
它不会停止测试,而是抛出这里的异常进行下面的
具体翻看文档
嵌套测试
翻文档
参数化测试
翻文档