Spring Boot开发教程
Spring Boot
可以轻松创建独立的、生产级的基于Spring的应用程序Spring Boot
直接嵌入Tomcat
、Jetty
或Undertow
,可以直接运行Spring Boot应用程序
SpringBoot快速入门
- 在
pom.xml
引入父工程
<!--导入SpringBoot父工程-->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.10</version>
</parent>
- 在
pom.xml
引入web启动器 - 导入web项目场景启动器,maven会自动导入与web开发相关的依赖jar包
<!--导入web项目场景启动器,会自动导入与web开发相关的依赖jar包-->
<dependencies>
<!--web场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--版本不用指定会与Springboot父工程保持一致-->
<!--<version>2.5.3</version>-->
</dependency>
</dependencies>
- SpringBoot启动器
package com.yangyi.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Projectname: SpringBootQiuckStart
* @Filename: MainApp
* @Author: 杨逸
* @Data:2023/9/25 17:15
* @Description: web项目启动器
*/
//表示这是一个springboot项目
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
//启动springboot应用程序
SpringApplication.run(MainApp.class,args);
}
}
SpringBoot
约定优于配置
-
自动配置
Spring
-
自动配置
Tomcat
-
自动配置字符过滤器等等
-
默认扫描主程序所在的包
-
指定特定要扫描的包
//使用属性scanBasePackages指定特定要扫描的包
@SpringBootApplication(scanBasePackages = {"com.yangyi"})
SpringBott的核心配置文件application.properties
配置文件
- 通过该配置文件可以进行
SpringBoot
默认配置的修改,如果有需要 - 通过注解
@Value(value = "name")
可以将核心配置中的值注入到bean的简单属性中,比如字符串或者数值 - 通过注解
@ConfigurationProperties(prefix = "monster")
可以将核心配置中的值注入到bean的复杂属性中,比如对象类型的属性 - 常用的配置
#端口号
server.port=10000
#应用的上下文路径(项目路径)
server.servlet.context-path=/allModel
#指定 POJO 扫描包来让 mybatis 自动扫描到自定义的 POJO
mybatis.type-aliases-package=com.cxs.allmodel.model
#指定 mapper.xml 的路径
#(application 上配置了@MapperScan(扫面 mapper 类的路径)和 pom.xml 中放行了 mapper.xml 后,
# 配 置 mapper-locations 没 有 意 义 。 如 果 mapper 类 和 mapper.xml 不 在 同 一 个 路 径 下 时 ,
mapper-locations 就有用了)
mybatis.mapper-locations=classpath:com/cxs/allmodel/mapper
#session 失效时间(单位 s)
spring.session.timeout=18000
#数据库连接配置
#mysql 数据库 url
mysql.one.jdbc-url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai&useSSL=false
#mysql 数据库用户名
mysql.one.username=
#数据库密码
mysql.one.password=
#线程池允许的最大连接数
mysql.one.maximum-pool-size=15
#日志打印:日志级别 trace<debug<info<warn<error<fatal 默认级别为 info,即默认打印 info 及其以
上级别的日志
#logging.level 设置日志级别,后面跟生效的区域,比如 root 表示整个项目,也可以设置为某个包下,
也可以具体到某个类名(日志级别的值不区分大小写)
logging.level.com.cxs.allmodel.=debug
logging.level.com.cxs.allmodel.mapper=debug
logging.level.org.springframework.web=info
logging.level.org.springframework.transaction=info
logging.level.org.apache.ibatis=info
logging.level.org.mybatis=info
logging.level.com.github.pagehelper = info
logging.level.root=info
#日志输出路径
logging.file=/tmp/api/allmodel.log
#配置 pagehelper 分页插件
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
#jackson 时间格式化
spring.jackson.serialization.fail-on-empty-beans=false
#指定日期格式,比如 yyyy-MM-dd HH:mm:ss,或者具体的格式化类的全限定名
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
#指定日期格式化时区,比如 America/Los_Angeles 或者 GMT+10
spring.jackson.time-zone=GMT+8
#设置统一字符集
spring.http.encoding.charset=utf8
#redis 连接配置
# redis 所在主机 ip 地址
spring.redis.host=
#redis 服务器密码
spring.redis.password=
#redis 服务器端口号
spring.redis.port=
#redis 数据库的索引编号(0 到 15)
spring.redis.database=14
## 连接池的最大活动连接数量,使用负值无限制
#spring.redis.pool.max-active=8
#
## 连接池的最大空闲连接数量,使用负值表示无限数量的空闲连接
#spring.redis.pool.max-idle=8
#
## 连接池最大阻塞等待时间,使用负值表示没有限制
#spring.redis.pool.max-wait=-1ms
#
## 最小空闲连接数量,使用正值才有效果
#spring.redis.pool.min-idle=0
#
## 是否启用 SSL 连接. ##spring.redis.ssl=false
#
## 连接超时,毫秒为单位
#spring.redis.timeout= 18000ms
#
## 集群模式下,集群最大转发的数量
#spring.redis.cluster.max-redirects=
#
## 集群模式下,逗号分隔的键值对(主机:端口)形式的服务器列表
#spring.redis.cluster.nodes=
#
## 哨兵模式下,Redis 主服务器地址
spring.redis.sentinel.master=
#
## 哨兵模式下,逗号分隔的键值对(主机:端口)形式的服务器列表
spring.redis.sentinel.nodes= 127.0.0.1:5050,127.0.0.1:5060
SpringBoot
中的注解
@Configuration
:标注在类上表示是配置类,创建配置类注入容器@Bean
:标注在方法上向容器注入一个bean,默认以方法名为该bean的id,也可以通过属性value或者name配置bean的id@Scope
:通过该注解配置多实例
package com.yangyi.springboot.bean.config;
import com.yangyi.springboot.bean.Monster;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Projectname: SpringBootQiuckStart
* @Filename: BeanConfig
* @Author: 杨逸
* @Data:2023/9/27 8:48
* @Description: 配置类
*/
//通过注解 @Configuration 表示该类是配置类
@Configuration
public class BeanConfig {
/**
* 使用@Bean注解,将方法返回值注入到Spring容器中
* 默认方法名是bean的id,也可以通过@Bean注解的name属性指定bean的id
* 也可以通过属性value指定bean的id
* 默认是单实例,如果需要多实例,可以配置scope属性
* @return
*/
@Bean
public Monster monster01(){
return new Monster(1,"牛魔王",100,"吹牛");
}
}
@import
:配合注解@Configration
,通过该注解注入组件- 注入bean的默认id是全类名
//通过@Import导入Bean,value属性是一个Class类型的数组,注入bean的默认id是全类名
@Import(value = {Dog.class, Cat.class})
@Configuration
public class BeanConfig2 {
}
Conditional
条件注解,与@Bean
注解配合使用,只有满足特定条件才会注入该bean- 一般使用的是
@Contitional
注解的扩展注解,比如ContitionalOnBean
注解 - 标注在配置类上表示该配置类要注入的bean都要满足特定的条件才会注入
- 标注在方法上表示该方法注入的bean要满足特定条件才会注入
package com.yangyi.springboot.bean.config;
import com.yangyi.springboot.bean.Monster;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Projectname: SpringBootQiuckStart
* @Filename: BeanConfig3
* @Author: 杨逸
* @Data:2023/9/27 9:45
* @Description: 配置类
*/
@Configuration
public class BeanConfig3 {
//通过 @ConditionalOnBean 注解指定只有当ioc容器存在id为monster01的bean时,才会注入该方法指定的bean
@ConditionalOnBean(name = "monster01")
@Bean
public Monster monster02(){
return new Monster(2,"孙悟空",200,"大闹天空");
}
}
ImportResource
注解关联spring
的bean.xml
配置文件
package com.yangyi.springboot.bean.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
* @Projectname: SpringBootQiuckStart
* @Filename: BeanConfig4
* @Author: 杨逸
* @Data:2023/9/27 10:16
* @Description: 配置类
*/
//通过ImportResource导入bean.xml
@ImportResource(value = {"classpath:bean.xml"})
@Configuration
public class BeanConfig4 {
}
- 使用
@ConfigurationProperties
注解进行配置绑定
package com.yangyi.springboot.bean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Projectname: SpringBootQiuckStart
* @Filename: Furn
* @Author: 杨逸
* @Data:2023/9/27 10:25
* @Description: 测试实体
*/
//通过@ConfigurationProperties注解完成配置绑定,读取application.properties文件中的配置进行bean属性的配置
@ConfigurationProperties(prefix = "furn01")
@Component
public class Furn {
private Integer id;
private String name;
private Double price;
public Furn() {
}
public Furn(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Furn{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
application.properties
配置文件
#配置绑定
furn01.id=1
furn01.name=空调
furn01.price=100.89
Lombok
中注解的使用
- 常用的注解
注解 | 说明 |
---|---|
@Getter | 在编译时,自动生成getter()方法 |
@Setter | 在编译时,自动生成setter()方法 |
@ToString | 在编译时,自动生成ToString()方法 |
@NoArgsConstructor | 在编译时,自动生成无参构造器 |
@AllArgsConstructor | 在编译时,自动生成全参构造器 |
@Data | 等价于@Getter ,@Setter ,@ToString ,@EqualsAndHashCode 注解的组合 |
@Log4j | 为类提供一个属性名为log的log4j日志对象 |
@CleanUp | 可以关闭流 |
@Buulder | 被注解的类加个建造者模式 |
@Synchronized | 加个同步锁 |
@SneakyThrows | 等价于try/catch 捕获异常 |
@NotNull | 如果给参数加这个注解,参数为null时会抛出空指针异常 |
@Value | 与@Data 注解类似,区别在于会把所有属性定义为private final 修饰,且没有setter方法 |
@Slf4j | 提供一个名为log的slf4j日志对象 |
- 日志对象的使用
package com.yangyi;
import com.yangyi.springboot.bean.Furn;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
/**
* @Projectname: SpringBootQiuckStart
* @Filename: HiController
* @Author: 杨逸
* @Data:2023/9/26 15:48
* @Description: 测试springboot默认扫描包的机制
*/
@Controller
@Slf4j
public class HiController {
@Resource
private Furn furn = null;
@RequestMapping(value = "/hi")
@ResponseBody
public String hi(){
return "hi,springboot,test1";
}
@ResponseBody
@RequestMapping(value = "/furn")
public Furn getFurn(){
log.info("furn=={}",furn);
return furn;
}
}
Spring Initailizr
的使用
- 创建SpringBoot Maven项目的模板
- 创建项目时会自动引入父工程和场景启动器等,一些必要的配置,减少手动配置的麻烦
- idea使用Spring Initializr模板创建项目
YAML语言
- YAML以数据做为中心,而不是以标记语言为重点
- YAML仍然是一种标记语言,但是和传统的标记语言不一样,是以数据为中心的标记语言
- YAML非常适合用来做以数据为中心的配置文件,比如:springboot:application.yaml
- Java使用YAML的文档
yaml的基本语法
- 形式为key:value.注意:后面有空格,区分大小写
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格[有些地方也识别tab,推荐使用空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- 字符串无需加引号
- 注释使用的是
#
,只支持单行注释
yaml的数据类型
- 字面量:单个的、不可再分的值.比如:
date、boolean、string、number、null
#字面量
id: 1
name: 牛魔王
- 对象:键值对的集合.比如
map、hash、set、object
#对象
#行内写法
k: {k1: v1,k2: v2}
monster: {id: 1,name: 牛魔王}
#行间写法
k:
k1: v1
k2: v2
monster:
id: 2
name: 狐狸精
- 数组:一组按次序排列的值.比如:
array、Iist、queue
#数组
#行内写法
array:{1,2,3}
#行间写法,需要在元素前加上字符 '-'
array:
- 1
- 2
- 3
yaml应用实例
- 完成bean的数据绑定
package com.yangyi.springboot.bean;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @Projectname: configuration
* @Filename: Monster
* @Author: 杨逸
* @Data:2023/9/28 10:45
* @Description: 演示yaml使用的实体类
*/
@ConfigurationProperties(prefix = "monster")
@Component
@Data
public class Monster {
private Integer id;
private String name;
private Integer age;
private Boolean isMarried;
private Date birthday;
private Cat cat;
private String[] skills;
private List<String> hobby;
private Map<String,Object> wife;
private Set<Double> salary;
private Map<String,List<Cat>> cats;
}
package com.yangyi.springboot.bean;
import lombok.Data;
/**
* @Projectname: configuration
* @Filename: Cat
* @Author: 杨逸
* @Data:2023/9/28 10:43
* @Description: 演示yaml使用的实体类
*/
@Data
public class Cat {
private String name;
private Double price;
}
- 引入SpringBoot配置文件处理器
<!--引入配置绑定处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<!--版本不用指定会与Springboot父工程保持一致-->
<!--防止依赖传递到其他模块-->
<optional>true</optional>
</dependency>
application.yaml
文件
#对象
monster:
#字面量
id: 1
name: 牛魔王
age: 1000
isMarried: true
birthday: 2019/01/12
#对象
cat: {name: 兰博基尼, price: 10000000}
#数组
skills: [吹牛, 打篮球]
hobby:
- 吃饭
- 喝水
wife:
key1: 爱丽丝
key2: 鲍勃
salary:
- 1000
- 200
- 300
cats:
cat1:
- {name: 宝马,price: 1001}
- {name: 奔驰,price: 1901}
cat2:
- {name: 奥迪,price: 1002}
- name: 劳斯莱斯
price: 1299
server:
port: 9999
SpringBoot
中的静态资源的访问
- 在类加载路径下的
/static
或/public
或/resources
或/META-INF/resources
下的静态资源可以被直接访问,对应WebPorperties.java
文件中的配置 - 静态资源访问的流程,先找控制器是否有匹配的路径,如果有就执行控制器的方法,如果没有就查找静态资源目录,如果找到就返回,没有则报错
- 可以通过
spring.mvc.static-path-pattern
修改访问静态资源路径前缀
#修改静态资源访问前缀
spring:
mvc:
static-path-pattern: /resourceTest/**
- 通过
spring.web.resources.static-locations
修改可以直接访问的静态资源路径 - 注意修改会覆盖默认的路径,可以手动将默认的路径添加上
#修改静态资源访问前缀
spring:
mvc:
static-path-pattern: /resourceTest/**
#修改静态资源的存放路径
web:
resources:
#修改静态资源访问路径,会覆盖默认的路径,需要手动将默认的路径添加上
static-locations: ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/",classpath:/imgs/]
SpringBoot
中的Rest风格请求
- 与springMVC中的语法一致,也需要配置
HiddenMethodFilter
过滤器
spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启隐藏HttpMethodFilter,支持表单的Restful风格请求
package com.yangyi.springboot.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @Projectname: SpringBootWeb
* @Filename: MonsterController
* @Author: 杨逸
* @Data:2023/9/30 12:52
* @Description: 演示Rest风格请求的控制器
*/
@RestController
public class MonsterController {
@GetMapping("/monster")
public String getMonster(){
return "get---monster";
}
@PostMapping("/monster")
public String postMonster(){
return "post---monster";
}
@PutMapping("/monster")
public String putMonster(){
return "put---monster";
}
@DeleteMapping("/monster")
public String deleteMonster(){
return "delete---monster";
}
}
SpringBoot
中使用RestTemplate
发起http请求
RestTemplate
是Spring
提供的用于访问Rest
服务的模板类,类似于JdbtTemplate
用访问数据库RestTemplate
提供了多种便捷访问远程Http服务的方法- 通过
RestTemplate
,我们可以发出http
请求(支持Restful,风格),去调用Controller
提供的API接口,就像我们使用浏览器发出http
请求,调用该API
接口一样
package com.yangyi.springboot.controller;
import com.yangyi.springboot.entity.Member;
import com.yangyi.springboot.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @Projectname: e-commerce-center
* @Filename: ConsumerController
* @Author: 杨逸
* @Data:2023/10/4 15:15
* @Description: 控制器
*/
@RestController
@Slf4j
public class ConsumerController {
//定义一个会员服务模块的基础url
private final String MEMBER_SERVICE_URL = "http://127.0.0.1:10000";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/getMemberById/{id}")
public Result getMemberById(@PathVariable("id") Long id){
//使用RestTemplate向会员服务模块发起get类型的http请求
//第一个参数是会员服务模块的url地址,第二个参数是响应参数的类型,第三个参数是返回值类型
Result result = restTemplate.getForObject(MEMBER_SERVICE_URL + "/member/getMemberById/" + id, null, Result.class);
//将会员服务模块返回的结果返回给调用者
return result;
}
@PostMapping("/consumer/saveMember")
public Result saveMember(@RequestBody Member member){
//使用RestTemplate向会员服务模块发起post类型的http请求
//第一个参数是会员服务模块的url地址,第二个参数是请求携带的参数,第三个参数是返回值类型
Result result = restTemplate.postForObject(MEMBER_SERVICE_URL + "/member/saveMember", member, Result.class);
return result;
}
}
SpringBoot
中配置视图解析器
- 需要注意,视图解析器
prefix
前缀需要与static-path-pattern
静态资源路径前缀保持一致
spring:
mvc:
static-path-pattern: /resourceTest/** #修改静态资源的存放路径
view: #配置视图解析器
prefix: /resourceTest/ #修改视图解析前缀,需要与static-path-pattern静态资源路径前缀保持一致
suffix: .html
SpringBoot
中接受参数的注解使用
- 主要的注解有:
@PathVariable
,@RequestHeader
,@ModelAtrribute
,@RequestParam
,@CookieValue
,@RequestBody
,@RequestAttribute
,@SessionAttribute
- 应用案例:
- 测试的接口
package com.yangyi.springboot.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Projectname: SpringBootWeb
* @Filename: ParamController
* @Author: 杨逸
* @Data:2023/9/30 14:09
* @Description: 演示Springboot获取参数的控制器
*/
@RestController
public class ParamController {
/**
*
* @param id 路径参数中的id参数
* @param name 路径参数中的name参数
* @param map 将路径参数和请求参数封装到map中
* @return
*/
@GetMapping("/pathVariable/{id}/{name}")
public String pathVariable(@PathVariable("id") Integer id, @PathVariable("name") String name,@PathVariable Map<String,String> map){
System.out.println("id = " + id);
System.out.println("name = " + name);
return "success";
}
/**
*
* @param host 发起请求的主机
* @param headerMap 请求头的所有参数封装到一个map中
* @return
*/
@GetMapping("/header")
public String header(@RequestHeader("Host") String host,@RequestHeader Map<String,String> headerMap){
System.out.println("host = " + host);
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
System.out.println("entry key= " + entry.getKey() + " values= " + entry.getValue());
}
return "success";
}
/**
*
* @param name 请求中的name参数
* @param hobby 请求中的hobby参数
* @param map 将请求中的所有参数封装到map中,注意存在多个同名参数时,只能获取一个,因为同一个key只能有一个值
* @return
*/
@GetMapping("/param")
public String param(@RequestParam("name") String name, @RequestParam("hobby") List<String> hobby, @RequestParam Map<String,String> map){
System.out.println("name = " + name);
for (String s : hobby) {
System.out.println("s = " + s);
}
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("entry = " + entry.getKey() + " values = " + entry.getValue());
}
return "success";
}
/**
*
* @param cookie_val 指定cookie的值
* @param cookie cookie对象
* @return
*/
@GetMapping("/cookie")
public String cookie(@CookieValue(value = "name") String cookie_val, @CookieValue(value = "user") Cookie cookie){
System.out.println("cookie_val = " + cookie_val);
System.out.println("cookie = " + cookie.getName() + " =" + cookie.getValue());
return "success";
}
/**
* 使用 @RequestBody 注解将表单中参数按字符串获取,也可以将表单中参数封装为对象
* @param name
* @return
*/
@PostMapping("/requestBody")
public String requestBody(@RequestBody(required = false) String name){
System.out.println("name = " + name);
return "success";
}
}
package com.yangyi.springboot.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @Projectname: SpringBootWeb
* @Filename: RequestController
* @Author: 杨逸
* @Data:2023/9/30 14:43
* @Description: 演示获取request域和session域的值
*/
@Controller
public class RequestController {
/**
* 设置参数的方法
* @param request
* @param session
* @return
*/
@RequestMapping("/login")
public String login(HttpServletRequest request, HttpSession session){
//设置request域的值
request.setAttribute("name","杨逸");
//设置session域的值
session.setAttribute("name","韩顺平");
return "forward:ok";
}
/**
* 获取request域和session域的值
* @param requestAttribute
* @param sessionAttribute
* @return
*/
@RequestMapping("/ok")
@ResponseBody
public String ok(@RequestAttribute(value = "name",required = false) String requestAttribute, @SessionAttribute(value = "name",required = false) String sessionAttribute){
System.out.println("requestAttribute = " + requestAttribute);
System.out.println("sessionAttribute = " + sessionAttribute);
return "success";
}
}
- 测试的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index页面</title>
</head>
<body>
<h1>演示SpringBoot获取参数的页面</h1>
<a href="/pathVariable/100/king">@PathVariable注解获取路径参数</a><br>
<a href="/header">@RequestHeader注解获取请求头的参数</a><br>
<a href="/param?name=杨逸&hobby=篮球&hobby=跳舞">@RequestParam注解获取请求中的参数</a><br>
<a href="/cookie">@CookieValue注解获取请求中的Cookie</a><br>
<!--<a href="/requestBody?name=hsp">@RequestBody注解按字符串获取请求中的参数,也可以按json格式封装为对象</a><br>-->
<a href="/login">@RequestAttribute注解获取request域中的数据</a><br>
<a href="/login">@SessionAttribute注解获取session域中的数据</a><br>
<hr>
<hr>
<form action="/requestBody" method="post">
姓名:<input type="text" name="name"><br>
<input type="submit">
</form>
</body>
</html>
SpringBoot
中获取复杂参数
- 比如复杂参数:
Map、Model、Errors/BindingResult、RedirectAttributes、ServletResponse、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder、HttpSession
- 请求的参数都会方法request域中,Map和Modle中的数据也会放到request域中
/**
* 模拟登陆的接口
* @return
*/
@RequestMapping("/register")
public String register(Map<String,String> map, Model model, HttpServletResponse response){
//向map添加信息
map.put("username","杨逸");
map.put("password","123456");
//向model添加信息
model.addAttribute("salary","10000");
//向response添加cookie
response.addCookie(new Cookie("email","yangyi@163.com"));
return "forward:/registerOk";
}
@RequestMapping("/registerOk")
@ResponseBody
public String registerOk(HttpServletRequest request){
//从request域中获取信息
System.out.println("request.getAttribute(\"username\") = " + request.getAttribute("username"));
System.out.println("request.getAttribute(\"password\") = " + request.getAttribute("password"));
System.out.println("request.getAttribute(\"salary\") = " + request.getAttribute("salary"));
return "success";
}
SpringBoot
中获取自定义对象参数
- 在开发中,SpringBoot在响应客户端/浏览器请求时,也支持自定义对象参数
- 完成自动类型转换与格式化
- 支持级联封装
- 案例
@RequestMapping("/save")
public String saveMonster(Monster monster){
System.out.println("monster = " + monster);
return "success";
}
- 测试页面
- 注意级联属性的赋值方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>获取自定义对象的测试页面</title>
</head>
<body>
<form action="/save" method="post">
编号:<input type="text" name="id" value="1"><br>
姓名:<input type="text" name="name" value="张三"><br>
年龄:<input type="text" name="age" value="20"><br>
婚姻:<input type="text" name="isMarried" value="true"><br>
生日:<input type="text" name="birthday" value="1990/01/01"><br>
坐骑:<input type="text" name="car.name" value="汽车"><br>
价格:<input type="text" name="car.price" value="1000"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
自定义转换器
- 转换器可以实现数据类型间的转换
- Spring中内置多种基本转换器,如果有需要,我们也可以自定义转换器
- 注意:转换器是保存在Map中的,所有同一种类型的转换器只有一个,加入多个同类型转换器会被覆盖,只保留最后一个注册的转换器
- 实现自定义转换器,将String类型转换为Car类
package com.yangyi.springboot.config;
import com.yangyi.springboot.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Projectname: SpringBootWeb
* @Filename: WebConfig
* @Author: 杨逸
* @Data:2023/9/30 16:26
* @Description: 演示注入自定义转换器的配置类
*/
//启用Lite轻量模式
@Configuration(proxyBeanMethods = false)
public class WebConfig {
//转换器是保存在Map中的,所有同一种类型的转换器只有一个,加入多个同类型转换器会被覆盖,只保留最后一个注册的转换器
//1.注入一个转换器
@Bean
public WebMvcConfigurer webMvcConfigurer(){
//2.返回WebMvcConfigurer接口的匿名内部类
return new WebMvcConfigurer(){
//3.通过addFormatters()方法注入一个转换器
@Override
public void addFormatters(FormatterRegistry registry) {
//4.使用FormatterRegistry对象注册一个转换器
//增添加一个字符串转Car类型的转换器
registry.addConverter(new Converter<String, Car>() {
//5.实现转换方法
@Override
public Car convert(String source) {
//6.实现转换的的具体逻辑
if(!ObjectUtils.isEmpty(source)){
String[] split = source.split(",");
return new Car(split[0],Double.valueOf(split[1]));
}
return null;
}
});
}
};
}
}
- 测试的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>获取自定义对象的测试页面</title>
</head>
<body>
<form action="/save" method="post">
编号:<input type="text" name="id" value="1"><br>
姓名:<input type="text" name="name" value="张三"><br>
年龄:<input type="text" name="age" value="20"><br>
婚姻:<input type="text" name="isMarried" value="true"><br>
生日:<input type="text" name="birthday" value="1990/01/01"><br>
坐骑:<input type="text" name="car" value="好坐骑,3232"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
- 测试的接口
@RequestMapping("/save")
public String saveMonster(Monster monster){
System.out.println("monster = " + monster);
return "success";
}
处理JSON格式数据
- 使用
@ResponseBody
注解可以返回客户端指定需要的数据,如果请求头中的Accept
属性是*/*
则默认返回JSON格式的数据 - 使用
@RequestBody
注解可以接受并解析客户端发送过来指定格式的数据
内容协商
- 客户端是通过请求头中的
Accept
属性指定可接受的数据类型,默认是*/*
可以接受所有的数据类型,默认返回的是JSON格式的数据是因为后端无法处理其他格式的数据
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
,Accept属性的的详解:数据类型的排列顺序表示可接受数据类型的优先级,q=0.9
:表示权重为0.9,如果优先级高的数据类型无法处理,则处理优先级次一级的,如果都可以处理,则处理权重高的- 客户端是通过请求头中的
ContentType
属性指定发送的数据类型 - 案例:通过指定
Accept
接受返回xml
格式的数据 - 返回
xml
格式的数据,需要引入处理xml
的jar
包
<!--引入处理xml的jar包-->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
- 测试接口
@RequestMapping("/getMonster")
@ResponseBody
public Monster getMonster() {
Monster monster = new Monster();
monster.setId(1);
monster.setName("小怪兽");
monster.setAge(10);
monster.setBirthday(new Date());
Car car = new Car();
monster.setCar(car);
car.setName("小汽车");
car.setPrice(7878.43);
return monster;
}
- 测试结果
开启基于请求参数的内容协商功能
- 浏览器的发起的请求我们无法修改其
Accept
的值,我们又要返回指定格式的数据时,可以使用基于请求参数的内容协商功能 - 开启基于请求参数的内容协商功能,并指定请求参数的名称,默认是:
format
spring:
mvc:
contentnegotiation:
favor-parameter: true #开启支持请求参数中包含MediaType
parameter-name: hspformat #指定MediaType请求参数名称,默认是format,指定为hspformat
- 测试默认的请求参数返回
json
格式数据:http://localhost:8080/getMonster?format=json
- 测试指定的请求参数返回
xml
格式数据:http://localhost:8080/getMonster?hspformat=xml
Thymeleaf(服务端渲染技术)
- 在SpringMVC中充当view视图
- 若要使用
Thymeleaf
语法,首先要声明名称空间:xmIns:th="http://www.thymeleaf.org"
- 设置文本内容
th:text
,设置input
的值th:value
,循环输出th:each
,条件判断th:if
,插入代码块th:insert
,定义代码块th:fragment
,声明变量th:object
th:each
的用法需要格外注意,打个比方:如果你要循环一个div中的p标签,则th:each
属性必须放在p标签上,若你将th:each
属性放在div上,则循环的是将整个div。- 变量表达式中提供了很多的内置方法,该内置方法是用
#
开头,请不要与#{}
消息表达式弄混。
Thymeleaf
语法
- 表达式
表达式名称 | 语法 | 用途 |
---|---|---|
变量取值 | ${} | 获取request域,session域,对象等的值 |
选择变量 | *{} | 获取上下文变量 |
消息 | #{} | 获取国际化等值 |
链接 | @{} | 生成链接 |
片段表达式 | ~{} | jsp:include的作用,引入公共页面片段 |
-
运算符
- 数学运算符
+
-
*
/
%
- 逻辑运算符
- 与:
and
- 或:
or
- 非:
!
,not
- 与:
- 比较运算符
关键字 运算符 gt > ge >= eq == lt < le <= ne != - 条件运算符
If-then:(if)?(then)
If-then-else:(if)?(then):(else)
Deafult:(value)?:(defaultvalue)
- 数学运算符
Thymeleaf
综合案例
- 用户管理系统
- 登陆页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登陆页面</title>
<style>
h1{
text-align: center;
}
div{
width: 500px;
height: 500px;
/*border: 1px solid red;*/
margin: 50px auto;
text-align: center;
}
h3{
margin-top: 50px;
}
form{
margin-top: 50px;
}
</style>
</head>
<body>
<h1>登陆页面</h1>
<div>
<h3>用户登录</h3>
<!--提交的超链接-->
<form action="#" th:action="@{/login}" method="post">
<!--错误信息的回显-->
<label style="color:red" th:text="${msg}"></label><br>
用户名:<input type="text" name="username"><br><br>
密码:<input type="password" name="password"><br><br>
<input type="submit" value="登录">
<input type="reset" value="重新填写">
</form>
</div>
</body>
</html>
- 管理页面
<!DOCTYPE html>
<!--引入thymeleaf命名空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8">
<title>管理页面</title>
<style>
h1{
text-align: center;
}
div{
width: 500px;
/*border: 1px solid red;*/
margin: 50px auto;
text-align: center;
}
h3{
margin-top: 50px;
}
th,td{
text-align: center;
width: 100px;
border: antiquewhite solid 1px;
background: aliceblue;
}
</style>
</head>
<body>
<h1>管理页面</h1>
<!--超链接表达式,以及条件判断-->
<pre> <a href="#" th:href="@{/login}">返回首页</a> <a href="#" th:href="@{/logout}">安全退出</a> 欢迎:<span th:if="${session.admin}" th:text="${session.admin.username}">xxx</span></pre>
<hr>
<div>
<h3>管理雇员</h3>
<table>
<tr>
<th>id</th>
<th>name</th>
<th>pwd</th>
<th>email</th>
<th>age</th>
</tr>
<!--遍历列表的显示-->
<tr th:each="user:${users}">
<td th:text="${user.id}">1</td>
<td th:text="${user.username}">xx</td>
<td th:text="${user.password}">xx</td>
<td th:text="${user.email}">xx</td>
<td th:text="${user.age}">xx</td>
</tr>
</table>
</div>
<hr>
</body>
</html>
Springboot
中的拦截器
-
在
Spring Boot
项目中,拦截器是开发中常用手段,要来做登陆验证、性能检查、日志记录等 -
基本步骤
- 编写一个拦截器实现HandlerInterceptor接口
- 拦截器注册到配置类中(实现WebMvcConfigurer的addInterceptors)
- 指定拦截规则
- 回顾SpringMVC中讲解的Interceptor
-
拦截器
package com.yangyi.springboot.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Projectname: springboot-usersys
* @Filename: LoginInterceptor
* @Author: 杨逸
* @Data:2023/10/1 11:14
* @Description: 登陆拦截器
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("拦截器拦截请求的URL:"+ requestURI);
if (request.getSession().getAttribute("admin")!=null){
//用户已登陆,放行
return true;
}
//用户未登陆,跳转到登陆页面
request.setAttribute("msg", "请先登录");
request.getRequestDispatcher("/login").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle方法被执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion方法被执行");
}
}
- 注册拦截器方式一:
- 通过注入一个
WebMvcConfigurer
的内部类实现拦截器注册 - 通过
addPathPatterns()
方法配置拦截路径 - 通过
excludePathPatterns()
方法配置放行路径
package com.yangyi.springboot.config;
import com.yangyi.springboot.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Projectname: springboot-usersys
* @Filename: WebConfig
* @Author: 杨逸
* @Data:2023/10/1 11:22
* @Description: 配置类
*/
@Configuration
public class WebConfig {
@Bean
//注册拦截器方式一
public WebMvcConfigurer addInterceptor(){
return new WebMvcConfigurer() {
//重写addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
//创建拦截器
LoginInterceptor interceptor = new LoginInterceptor();
//注册拦截器
registry.addInterceptor(interceptor) //配置拦截规则
.addPathPatterns("/**") //拦截所有请求
.excludePathPatterns("/","/login","/imgs/**"); //不拦截的路径
}
};
}
}
- 注册拦截器方式二:
- 通过配置类实现
WebMvcConfigurer
接口,直接重写addInterceptors()
方法注册拦截器
package com.yangyi.springboot.config;
import com.yangyi.springboot.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Projectname: springboot-usersys
* @Filename: WebConfig
* @Author: 杨逸
* @Data:2023/10/1 11:22
* @Description: 配置类
*/
@Configuration
//通过配置类直接实现WebMvcConfigurer接口也可以注册拦截器
public class WebConfig implements WebMvcConfigurer{
//注册拦截器方式二
@Override
public void addInterceptors(InterceptorRegistry registry) {
//创建拦截器
LoginInterceptor interceptor = new LoginInterceptor();
//注册拦截器
registry.addInterceptor(interceptor) //配置拦截规则
.addPathPatterns("/**") //拦截所有请求
.excludePathPatterns("/","/login","/imgs/**"); //不拦截的路径
}
}
URI
与URL
的区别
URI = Universal Resource Identifier
- ``URL = Universal Resource Locator`
Identifier
:标识符,Locator
:定位器URI
可以唯一标识一个资源,URL
可以提供找到该资源的路径
SpringBoot
中的文件上传
- 页面
- 表单使用
enctype="multipart/form-data"
类型,用于上传文件 - 上传多个文件使用
multiple
属性
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<style>
h1{
text-align: center;
}
div{
width: 500px;
height: 500px;
/*border: 1px solid red;*/
margin: 50px auto;
text-align: center;
}
h3{
margin-top: 50px;
}
form{
margin-top: 50px;
}
</style>
</head>
<body>
<h1>用户注册</h1>
<div>
<form action="#" th:action="@{/upload}" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br><br>
密码:<input type="password" name="password"><br><br>
邮件:<input type="email" name="email"><br><br>
年龄:<input type="number" name="age"><br><br>
头像:<input type="file" name="header"><br><br>
<!--使用multiple,可以上传多个图片-->
宠物:<input type="file" name="pets" multiple><br><br>
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
</div>
</body>
</html>
- 接口
- 使用
MultipartFile
类型接收上传的文件,接受多个文件时使用MultipartFile
数组即可 - 使用
transferTo
方法将数据写入文件保存
package com.yangyi.springboot.controller;
import com.yangyi.springboot.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.util.UUID;
/**
* @Projectname: springboot-usersys
* @Filename: UploadController
* @Author: 杨逸
* @Data:2023/10/1 14:28
* @Description: 文件上传控制器
*/
@Controller
@Slf4j
public class UploadController {
@GetMapping("/upload.html")
public String uploadPage(){
log.info("uploadPage");
//转发到文件上传页面
return "upload";
}
/**
*
* @param user 用信息
* @param header 用户头像
* @param pets 用户宠物图片
* @return
*/
@PostMapping("/upload")
@ResponseBody
public String upload(User user, @RequestParam("header") MultipartFile header, @RequestParam("pets")MultipartFile[] pets) throws IOException, URISyntaxException {
//使用MultipartFile类型接受文件数据,多个文件使用数组接收
log.info("接受到的信息user={},header={},pets={}",user,header,pets);
//保存文件,保存到指定文件夹
//String path = "D:\\2023\\Spring Boot\\upload\\";
//动态创建文件夹保存文件,保存在类路径下的/static/imgs/文件下
//String path = ResourceUtils.getURL("classpath:").toURI().getPath();
String path = getClass().getClassLoader().getResource("").toURI().getPath();
log.info("类路径path={}",path);
path = path + "static/imgs/";
//拼接日期创建文件夹
for (String date : LocalDate.now().toString().split("-")) {
path += date + "/";
}
log.info("文件夹路径path={}",path);
//创建文件夹
File folder = new File(path);
if (!folder.exists())folder.mkdirs();
//头像图片
if (!header.isEmpty()){
//创建文件对象,使用UUID和时间戳防止文件重名
File file = new File(path + UUID.randomUUID().toString()+"-"+ System.currentTimeMillis() +"-"+ header.getOriginalFilename());
//将数据写入文件
header.transferTo(file);
}
//宠物图片
if (pets.length>0){
for (MultipartFile pet:pets){
if (!pet.isEmpty()){
//创建文件对象
File file = new File(path + UUID.randomUUID().toString()+"-"+ System.currentTimeMillis() +"-"+ pet.getOriginalFilename());
//将数据写入文件
pet.transferTo(file);
}
}
}
log.info("保存文件成功");;
return "upload success";
}
}
- 配置上传文件的大小限制
spring:
servlet:
multipart:
max-file-size: 10MB # 单个文件大小,默认是1MB
max-request-size: 100MB # 设置总上传的数据大小,默认是10MB
SpringBoot
中的异常处理
- 默认情况下,
Spring Boot
提供/error
处理所有错误的映射,也就是说当出现错误时,SpringBoot
底层会情求转发到/error
这个映射 - 局部异常
- 全局异常
- 默认异常
自定义异常页面
- 编写错误页面,以错误代码为页面文件的名称,并存放在静态资源路径下的'/error'路径下,发生错误时Springboot会自动去寻找对应错误代码的页面,如果没有则寻找
4xx.html
页面或者5xx.html
页面,找到则返回 - 假如发生404错误,
SpringBoot
会先去寻找404.html
错误页面,如果没有找到,则寻找4xx.html
页面,如果找到则直接返回
- 错误页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>404错误页面</title>
<style>
h1{
text-align: center;
}
div{
width: 500px;
height: 500px;
/*border: 1px solid red;*/
margin: 50px auto;
text-align: center;
}
h3{
margin-top: 50px;
}
</style>
</head>
<body>
<h1>错误页面</h1>
<div>
<h3>404 Not Found</h3>
<h3>状态码:<span th:text="${status}"></span></h3>
<h3>错误信息:<span th:text="${error}"></span></h3>
<p>对不起,您访问的页面不存在或已被删除</p>
<p>您可以:<a href="#" th:href="@{/}">返回首页</a></p>
</div>
</body>
</html>
- 测试的接口
package com.yangyi.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Projectname: springboot-usersys
* @Filename: MyErrorController
* @Author: 杨逸
* @Data:2023/10/1 16:53
* @Description: 模拟发生错误的控制器
*/
@Controller
public class MyErrorController {
/**
* 模拟服务器发生错误 状态码为500
* @return
*/
@RequestMapping("/errorByZero")
public String error(){
int i = 10/0;
return "/";
}
/**
* 模拟请求方法错误 状态码为405
* @return
*/
@PostMapping("/errorByPost")
public String error2(){
return "loginPage";
}
}
全局异常处理
@ControllerAdvice
+@ExceptionHandler
处理全局异常- 底层是
ExceptionHandlerExceptionResolver
异常解析器支持的 - 案例:
- 演示全局异常使用,当发生
ArithmeticException
、NullPointerException
时,不使用默认异常机制匹配的XXX.html
,而是通过全局异常机制显示指定的错误页面
package com.yangyi.springboot.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @Projectname: springboot-usersys
* @Filename: GlobalExceptionHandler
* @Author: 杨逸
* @Data:2023/10/1 17:10
* @Description: 全局异常处理器
*/
//使用@RestControllerAdvice注解,表示该类是全局异常处理器
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理算术异常
* @param e
* @param model
* @return
*/
@ExceptionHandler(value = {ArithmeticException.class})
public String handleAriteException(ArithmeticException e, Model model){
log.info("算术异常{}",e.getMessage());
//将异常信息传递给页面
model.addAttribute("msg",e.getMessage());
return "error/global";
}
}
SpringBoot
中自定义异常
- 如果Spring Boot提供的异常不能满足开发需求,程序员也可以自定义异常
- 使用
@ResponseStatus
注解自定义异常 - 底层是
ResponseStatusExceptionResolver
响应状态处理器,底层调用response.sendError(statusCode,resolvedReason)
- 当抛出自定义异常后,仍然会根据状态码,去匹配使用
XXx.html
显示
package com.yangyi.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @Projectname: springboot-usersys
* @Filename: AccessException
* @Author: 杨逸
* @Data:2023/10/1 19:54
* @Description: 自定义访问异常
*/
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "没有权限,非法访问")
public class AccessException extends RuntimeException{
public AccessException() {
super();
}
public AccessException(String message) {
super(message);
}
}
Interceptor
拦截器与Filter
过滤器的区别
- 使用范围不同
- 过滤器实现的是
javax.servlet.Filter
接口,而这个接口是在Servlet
规范中定义的,也就是说过滤器Filter
的使用要依赖于Tomcat
等容器,Filter
只能在web
程序中使用 - 拦截器(
Interceptor
)它是一个Spring
组件,并由Spring
容器管理,并不依赖Tomcat
等容器,是可以单
独使用的。不仅能应用在web
程序中,也可以用于Application
等程序中
- 过滤器实现的是
- 触发时机不同
graph LR;
subgraph Tomcat;
subgraph Filter;
subgraph Servlet;
subgraph Interceptor;
subgraph Controller;
end
end
end
end
end
tomcat[(Tomcat)] --1--> filter[Filter];
filter --2--> servlet[Servlet];
servlet --3--> interceptor[Interceptor];
interceptor --4--> controller[(Controller)];
controller -.5.-> interceptor;
interceptor -.6.-> servlet;
servlet -.7.->filter;
filter -.8.->tomcat;
- 过滤器不会处理请求转发,拦截器会处理请求转发,因为请求转发经过了拦截器而没有经过过滤器
在SpringBoot
中注入Servlet
,Filter
,Listener
- 分别使用
@WebServlet
,@WebFilter
,@WebListener
注解注入Servlet
,Filter
,Listener
,使用这种方式注入的Servlet
不会被SpringBoot
的拦截器拦截,因为不会经过disPatcherServlet
前端分发控制器 - 因为一个请求先通过
tomcat
,然后通过filter
过滤器,然后通过servlet
,如果在servlet
匹配成功,就不需要通过disPatcherServlet
前端分发控制器,所有就不会通过拦截器,拦截器是基于disPatcherServlet
前端分发控制器的 - 将自定义Servlet注入到Spring容器中,需要使用
@ServletComponentScan
注解配置扫描路径才能注入
@ServletComponentScan(value = "com.yangyi.springboot")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
使用@WebServlet
注解注入Servlet
- 使用
@WebServlet
注解来标识一个Servlet
urlPatterns
属性指定要拦截的请求的路径
package com.yangyi.springboot.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Projectname: springboot-usersys
* @Filename: MyServlet
* @Author: 杨逸
* @Data:2023/10/1 20:17
* @Description: 自定义的servlet
*/
//使用@WebServlet注解来映射servlet
//urlPatterns属性指定要拦截的请求的路径
//将自定义Servlet注入到Spring容器中,需要使用@ServletComponentScan注解配置扫描路径才能注入
@WebServlet(urlPatterns = {"/myServlet01","/myServlet02"},name = "myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("自定义的servlet");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("自定义的servlet");
}
}
使用@WebFilter
注解注入Filter
- 在SpringBoot中,自定义过滤器需要实现Filter接口
- 使用
@WebFilter
注解标注一个过滤器 urlPatterns
属性指定需要拦截的请求路径
package com.yangyi.springboot.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @Projectname: springboot-usersys
* @Filename: Myfilter
* @Author: 杨逸
* @Data:2023/10/1 20:25
* @Description: 自定义过滤器
*/
//在SpringBoot中,自定义过滤器需要实现Filter接口
//使用@WebFilter注解标注过滤器,
//urlPatterns属性指定需要拦截的请求路径
//注入spring容器需要使用@ServletComponentScan注解
@WebFilter(urlPatterns = {"/*"})
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("自定义过滤器执行了");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("自定义过滤器执行结束了");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("自定义过滤器初始化了");
}
@Override
public void destroy() {
System.out.println("自定义过滤器销毁了");
}
}
使用@WebListener
注入Listener
- 实现具体的监听器接口
- 使用
@WebListener
注解标注一个监听器 - 将自定义
Listener
注入到Spring
容器中,需要使用@ServletComponentScan
注解配置扫描路径才能注入
package com.yangyi.springboot.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* @Projectname: springboot-usersys
* @Filename: MyListener
* @Author: 杨逸
* @Data:2023/10/1 20:40
* @Description: 自定义监听器
*/
//实现具体的监听器接口
//使用@WebListener注解标注一个Listener监听器
//将自定义Listener注入到Spring容器中,需要使用@ServletComponentScan注解配置扫描路径才能注入
@Slf4j
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("contextInitialized初始化成功");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("contextDestroyed销毁成功");
}
}
使用配置类注入原生Servlet
,Filter
,Listener
- 先编写
Servlet
,Filter
,Listener
- 通过注入对应的
XxxRegistrationBean
- 实例化对应的servlet
- 设置拦截
url
- 返回对应的
XxxRegistrationBean
- 注册原生
Servlet
,将原生Servlet
传入ServletRegistrationBean
,然后将其返回即可 - 注册原生
Filter
,使用FilterRegistrationBean
- 注册原生
Listener
,使用ServletListenerRegistrationBean
package com.yangyi.springboot.config;
import com.yangyi.springboot.servlet.MyFilter;
import com.yangyi.springboot.servlet.MyListener;
import com.yangyi.springboot.servlet.MyServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Projectname: springboot-usersys
* @Filename: MyRegisterConfig
* @Author: 杨逸
* @Data:2023/10/1 21:21
* @Description: 注入原生Servlet,Filter,Listener的配置类
*/
@Configuration
public class MyRegisterConfig {
/**
* 注册原生Servlet,使用ServletRegistrationBean
* @return
*/
@Bean
public ServletRegistrationBean<MyServlet> myServlet(){
//实例化servlet
MyServlet myServlet = new MyServlet();
//实例化servlet注册bean,配置拦截url
ServletRegistrationBean<MyServlet> servletRegistrationBean = new ServletRegistrationBean<>(myServlet, "/myServlet03","/myServlet04");
//返回
return servletRegistrationBean;
}
/**
* 注册原生Filter,使用FilterRegistrationBean
* @return
*/
@Bean
public FilterRegistrationBean<MyFilter> myFilter(){
//实例化filter
MyFilter myFilter = new MyFilter();
//实例化filter注册bean,
FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>(myFilter);
//配置拦截url
filterRegistrationBean.addUrlPatterns("/*","/template/*");
//返回
return filterRegistrationBean;
}
/**
* 注册原生Listener,使用ServletListenerRegistrationBean
* @return
*/
@Bean
public ServletListenerRegistrationBean<MyListener> myListener(){
//实例化listener
MyListener myListener = new MyListener();
//实例化listener注册bean
ServletListenerRegistrationBean<MyListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(myListener);
//返回
return servletListenerRegistrationBean;
}
}
SpringBoot
中Tomcat
常用配置
- 通过配置文件配置
Tomcat
Tomcat
的配置与ServerPropesties.java
配置类相关联
server:
port: 9999 #配置服务端口
tomcat: #配置tomcat常用配置
threads: #配置工作线程数
min-spare: 10 #最小工作线程数,默认是10
max: 200 #最大工作线程数,默认是200
accept-count: 100 #最大等待队列数,默认是100
max-connections: 8192 #最大连接数(并发数),默认是8192
connection-timeout: 10000 #连接超时时间,默认没有设置,单位为毫秒
- 通过配置类配置
Tomcat
package com.yangyi.springboot.config;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Configuration;
/**
* @Projectname: springboot-usersys
* @Filename: MyTomcatConfig
* @Author: 杨逸
* @Data:2023/10/2 10:50
* @Description: 通过配置类配置tomcat
*/
@Configuration
public class MyTomcatConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
//设置服务端口
factory.setPort(8888);
//可以通过ConfigurableServletWebServerFactory类的一系列方法配置tomcat
}
}
切换Web
服务器
SpringBoot
的默认Web
服务器是Tomcat
- 在
web
启动器中排除tomcat
服务器的jar
- 引入
undertow
服务器的jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 在web启动器中排除tomcat服务器的jar-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入undertow服务器的jar-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
SpringBoot
中的数据库操作
使用默认的数据源
- 引入需要的
jar
SpringBoot
默认使用的数据源是HikariDataSource
<!-- 进行数据库操作,引入spring-boot-starter-data-jdbc启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- 引入数据库对应的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
- 配置数据库源
spring:
datasource: #配置数据源
url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true
username: yangyi
password: 2004
driver-class-name: com.mysql.jdbc.Driver
- 测试
SpringBoot
程序的测试需要引入spring-boot-starter-test
测试启动器,并使用@SpringbootTest
注解标注测试类
<!--引入starter-test,测试SpringBoot项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
package com.yangyi.springboot;
import com.yangyi.springboot.bean.Furn;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.annotation.Resource;
import java.util.List;
/**
* @Projectname: springboot-usersys
* @Filename: ApplicationTest
* @Author: 杨逸
* @Data:2023/10/2 12:17
* @Description: springboot测试类
*/
@SpringBootTest
public class ApplicationTest {
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void jdbcTest(){
//创建一个映射类,帮助spring把数据库中的数据映射到java对象中
BeanPropertyRowMapper<Furn> rowMapper = new BeanPropertyRowMapper<>(Furn.class);
List<Furn> furns = jdbcTemplate.query("select * from furn", rowMapper);
for (Furn furn : furns) {
System.out.println("furn = " + furn);
}
System.out.println("jdbcTemplate.getDataSource() = " + jdbcTemplate.getDataSource());
}
}
Druid
数据源手动整合到Springboot
-
Druid:性能优秀,Druid提供性能卓越的连接池功能外,还集成了SQL监控,黑名单拦截等功能,强大的监控特性,通过Duid提供的监控功能,可以清楚知道连接池和SQL的工作情况
-
引入
druid
数据源的jar
<!-- 引入druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
- 注入
druid
数据源到spring
中 - 注入druid数据源,覆盖默认数据源
- 使用
@ConfigurationProperties
注解注入数据源的连接信息
package com.yangyi.springboot.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @Projectname: springboot-usersys
* @Filename: DruidDataSourceConfig
* @Author: 杨逸
* @Data:2023/10/2 12:37
* @Description: druid数据源配置类
*/
@Configuration
public class DruidDataSourceConfig {
//注入druid数据源,覆盖默认数据源
//使用@ConfigurationProperties注解注入数据源的连接信息
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
return dataSource;
}
}
开启Druid
的监控功能
Druid
的监控功能通过StatViewServlet
实现,我们需要注入一个StatViewServlet
- 通过
StatViewServlet
的addInitParameter
方法设置初始化参数,包括访问监控的用户名和密码
/**
* 注入一个druid的监控视图servlet,开启监控页面的访问
* @return
*/
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>(statViewServlet,"/druid/*");
//通过addInitParameter方法设置初始化参数
servletRegistrationBean.addInitParameter("allow","127.0.0.1");
//设置访问druid的用户名和密码
servletRegistrationBean.addInitParameter("loginUsername","admin");
servletRegistrationBean.addInitParameter("loginPassword","2004");
return servletRegistrationBean;
}
启用Druid
的sql监控
- 在注入数据源时,添加名为
stat
过滤器
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
//开启sql监控功能
dataSource.setFilters("stat");
return dataSource;
}
配置druid
的web
关联监控
- 启用
druid
的web
关联健康,可以监控请求的URI
- 注入一个
WebStatFilter
的监控过滤器,开启监控过滤器 - 通过
setUrlPatterns
方法设置监控的路径 - 通过
addInitParameter
方法添加初始化参数,设置不监控的路径
/**
* 启用druid的web关联健康,可以监控请求的URI
* 注入一个druid的监控过滤器,开启监控过滤器
* @return
*/
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilterFilter(){
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
//配置监控的路径
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
//配置不监控的路径
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.html,*.swf");
return filterRegistrationBean;
}
配置druid
的sql
防火墙
- 在注入数据源时,添加名为
wall
过滤器
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
//开启sql监控功能,开启sql防火墙功能
dataSource.setFilters("stat,wall");
return dataSource;
}
通过starter-druid
启动器整合Druid
数据源
- 官方文档
- 引入
starter-druid
启动器
<!-- 引入starter-druid启动器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
- 配置
druid
,并开启监控的功能
spring:
datasource: #配置数据源
url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true
username: yangyi
password: 2004
driver-class-name: com.mysql.jdbc.Driver
druid:
enable: true #1.是否启用druid,默认是false
stat-view-servlet: #2.配置druid的监控功能
login-username: admin #登录druid监控界面的用户名,默认是null
login-password: 2004 #登录druid监控界面的密码,默认是null
url-pattern: /druid/* #访问监控页面的路径,默认是/druid/*
enabled: true
web-stat-filter: #3.配置druid的web关联监控功能
enabled: true #是否启用web监控,默认是false
url-pattern: /* #配置监控的页面路径
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.html,*.swf" #配置忽略的页面
filter:
stat: #4.sql监控功能
enabled: true
slow-sql-millis: 1000 #配置sql慢查询的时间
log-slow-sql: true #是否开启慢查询日志
wall: #5.sql防火墙功能
enabled: true
config:
drop-table-allow: false #是否允许使用drop table语句
select-all-column-allow: false #是否允许使用select *语句
-
启用
Druid
spring: datasource: #配置数据源 url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true username: yangyi password: 2004 driver-class-name: com.mysql.jdbc.Driver druid: enable: true #1.是否启用druid,默认是false
-
配置监控功能
stat-view-servlet: #2.配置druid的监控功能 login-username: admin #登录druid监控界面的用户名,默认是null login-password: 2004 #登录druid监控界面的密码,默认是null url-pattern: /druid/* #访问监控页面的路径,默认是/druid/* enabled: true
-
web
关联监控web-stat-filter: #3.配置druid的web关联监控功能 enabled: true #是否启用web监控,默认是false url-pattern: /* #配置监控的页面路径 exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.html,*.swf" #配置忽略的页面
-
sql
监控功能filter: stat: #4.sql监控功能 enabled: true slow-sql-millis: 1000 #配置sql慢查询的时间 log-slow-sql: true #是否开启慢查询日志
-
sql
防火墙功能filter: wall: #5.sql防火墙功能 enabled: true config: drop-table-allow: false #是否允许使用drop table语句 select-all-column-allow: false #是否允许使用select *语句
SpringBoot
整合Mybatis
- 引入
mybatis
启动器
<!-- 引入mybatis启动器--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>
- 配置连接信息,数据源
server: port: 9999 spring: datasource: url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true username: yangyi password: 2004 driver-class-name: com.mysql.jdbc.Driver druid: enable: true
- 配置
XxxMapper.xml
映射文件路径
mybatis: # 指定mapper文件位置 mapper-locations: classpath:mapper/*.xml
- 编写
XxxMapper
接口 - 在
SpringBoot中
使用@Mapper
注解标注一个mybatis
映射接口,将接口注册到Spring
容器中
package com.yangyi.springboot.mapper; import com.yangyi.springboot.bean.Monster; import org.apache.ibatis.annotations.Mapper; /** * @Projectname: springboot_mybatis * @Filename: MonsterMapper * @Author: 杨逸 * @Data:2023/10/2 15:50 * @Description: 映射接口 */ //在SpringBoot中使用@Mapper注解标注一个mybatis映射接口,将接口注册到Spring容器中 @Mapper public interface MonsterMapper { Monster getMonsterById(Integer id); }
- 编写
XxxMapper.xml
文件
<?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"> <mapper namespace="com.yangyi.springboot.mapper.MonsterMapper"> <sql id="base_fileds">id,name,age,sex,birthday,email,salary</sql> <select id="getMonsterById" parameterType="java.lang.Integer" resultType="com.yangyi.springboot.bean.Monster"> select <include refid="base_fileds"/> from monster where id=#{id} </select> </mapper>
在
SpringBoot
中mybatis
的配置- 即可配置传统的
mybatis-config.xml
进行开发,也可以直接在application.yaml
文件上直接配置mybatis
的相关配置
mybatis: # 指定mapper文件位置 mapper-locations: classpath:mapper/*.xml #指定mybatis核心配置文件的配置,按照传统的方式进行开发, #也可以直接在application.yaml文件中直接进行mybatis的配置 #config-location: classpath:mybatis-config.xml #配置开启别名 type-aliases-package: com.yangyi.springboot.bean #配置mybatis的全局配置 configuration: #配置标准日志输出 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 解决时区问题,通过
@JsonFormat
注解
//通过@JsonFormat注解实现日期格式化 //GMT表示格林尼治标准时间,+8,是因为中国在东八区 @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") private Date birthday;
- 测试
package com.yangyi.springboot; import com.yangyi.springboot.bean.Monster; import com.yangyi.springboot.mapper.MonsterMapper; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import javax.annotation.Resource; /** * @Projectname: springboot_mybatis * @Filename: ApplicationTest * @Author: 杨逸 * @Data:2023/10/2 15:35 * @Description: 测试类 */ @SpringBootTest @Slf4j public class ApplicationTest { @Resource private JdbcTemplate jdbcTemplate; @Resource private MonsterMapper monsterMapper; @Test public void main() { log.info("数据源={}",jdbcTemplate.getDataSource()); } @Test public void getMonster(){ Monster monster = monsterMapper.getMonsterById(1); log.info("monster={}",monster); } }
- 引入
SpringBoot
整合Mybatis-plus
- MyBatis-PIus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生
- 强大的CRUD操作:内置通用Mapper、通用Service,通过少量配置即可实现单表大部分CRUD操作,再有强大的条件构浩器,满足各类使用需求
- 引入
mybatis-plus
启动器
<!-- 引入mybatis-plus启动器-->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
- 配置数据源
server:
port: 9999
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true
username: yangyi
password: 2004
driver-class-name: com.mysql.jdbc.Driver
druid:
enable: true
- 配置
mybatis-plus
,配置方式与mybatis
基本一致
mybatis-plus:
configuration:
# 配置日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置mapper文件路径,与mybatis一致
mapper-locations: classpath:mapper/*.xml
开发Mapper
接口
- 使用
@Mapper
标注为一个mybatis
映射接口并注入Spring
- 与
mybatis
不同的是,需要继承一个BaseMapper
基础类,并指定对应的泛型 BaseMapper
中实现了基本的curd操作,简化开发
package com.yangyi.springboot.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yangyi.springboot.bean.Monster;
import org.apache.ibatis.annotations.Mapper;
/**
* @Projectname: springboot_mybatis_plus
* @Filename: MonsterMapper
* @Author: 杨逸
* @Data:2023/10/2 17:21
* @Description: 映射接口
*/
//BaseMapper中已经定义了基本的增删改查方法,可以直接继承使用
//如果默认定义的方法不能满足需求,可以重写方法,也可以在新增方法,在对应Xxx.Mapper实现即可
@Mapper
public interface MonsterMapper extends BaseMapper<Monster> {
}
开发Service
层接口
mybatis-plus
的IService
定义了基础的crud
操作- 使用
mybatis-plus
的Service
层需要继承IService
接口,并指定泛型
package com.yangyi.springboot.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yangyi.springboot.bean.Monster;
/**
* @Projectname: springboot_mybatis_plus
* @Filename: MonsterService
* @Author: 杨逸
* @Data:2023/10/2 17:34
* @Description: 映射接口
*/
//mybatis-plus的IService定义基础的crud操作
//使用mybatis-plus的Service层需要继承IService,并指定泛型
public interface MonsterService extends IService<Monster> {
}
实现Service
层的接口
- 使用
mybatis-plus
的服务实现类需要继承ServiceImpl
,并指定泛型为对应的映射接口和对应的实体类 ServiceImpl
实现了IService
中的所有crud
方法,所以直接继承即可,继承后就不需要我们手动实现
package com.yangyi.springboot.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yangyi.springboot.bean.Monster;
import com.yangyi.springboot.mapper.MonsterMapper;
import com.yangyi.springboot.service.MonsterService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Projectname: springboot_mybatis_plus
* @Filename: MonsterServiceImpl
* @Author: 杨逸
* @Data:2023/10/2 17:49
* @Description: 接口实现
*/
//使用mybatis-plus的服务实现类需要继承ServiceImpl,并指定泛型为对应的映射接口和对应的实体类
// 再实现服务接口
//ServiceImpl实现了IService中的所有crud方法,所以直接继承即可
@Service
public class MonsterServiceImpl extends ServiceImpl<MonsterMapper, Monster> implements MonsterService {
@Resource
private MonsterMapper monsterMapper;
}
配置扫描mapper
接口
- 使用
@MapperScan
注解扫描所有mybatis
的dao
接口 - 如果不想使用扫描的方式,也可以使用
@Mapper
注解进行单独的注入
package com.yangyi.springboot;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @Projectname: springboot_mybatis_plus
* @Filename: Application
* @Author: 杨逸
* @Data:2023/10/2 17:18
* @Description: 主程序
*/
//使用@MapperScan注解扫描所有mybatis的dao接口
@MapperScan(value = {"com.yangyi.springboot.mapper"})
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new SpringApplication(Application.class).run(args);
System.out.println("(♥◠‿◠)ノ゙ 启动成功 ლ(´ڡ`ლ)゙ \n" +
" ____ _ _ \n" +
" / ___| ___ __ _ _ __| |__ ___ | |_ \n" +
" \\___ \\ / __/ _` | '__| '_ \\ / _ \\| __|\n" +
" ___) | (_| (_| | | | | | | (_) | |_ \n" +
" |____/ \\___\\__,_|_| |_| |_|\\___/ \\__|\n" +
" ");
System.out.println("项目启动成功!");
}
}
@TableName
注解的使用
- 用于配置实体类对应的表名,用于解决实体名与表名不一致的情况
- 默认实体类对应的表名为类名首字母小写,比如:
Monster --> monster
- 当类与表保持一致时,可以不添加该注解
package com.yangyi.springboot.bean;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @Projectname: springboot_mybatis_plus
* @Filename: Monster
* @Author: 杨逸
* @Data:2023/10/2 17:20
* @Description: 实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
//@TableName注解用于配置实体类对应的表名,用于解决实体名与表名不一致的情况
@TableName(value = "monster")
public class Monster {
private Integer id;
private Integer age;
private String name;
private String email;
//通过@JsonFormat注解实现日期格式化
//GMT表示格林尼治标准时间,+8,是因为中国在东八区
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date birthday;
private double salary;
private Character sex;
}
@TableId
注解的使用
- 使用该注解标注一个属性为表的主键
- 通过type属性设置主键的类型为自动增长类型
/**
* 使用@TableId(type = IdType.AUTO)注解标识一个属性为表的主键,
* 通过type属性设置主键的类型为自动增长类型
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
Mtbatis-plus
中的分页查询
- 注入一个
Myabtis-plus
拦截器,并指定数据库的类型- 创建一个
Myabtis-plus
拦截器 - 向
Myabtis-plus
拦截器注入一个分页拦截器 - 指定数据库的类型
- 创建一个
package com.yangyi.springboot.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Projectname: springboot_furn
* @Filename: MybatisPlusConfig
* @Author: 杨逸
* @Data:2023/10/3 14:38
* @Description: 配置类
*/
@Configuration
public class MybatisPlusConfig {
//注入一个分页插件,本质是一个拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//创建分页拦截器
MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor();
//创建一个内部拦截器,指定数据库类型
plusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return plusInterceptor;
}
}
- 进行分页查询
- 创建一个分页对象
- 使用
mybatis-plus
自带的分页查询方法进行分页查询
//分页查询的接口
@GetMapping("/furniture/list/page")
public Result listFurnitureByPage(@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "5") Integer pageSize){
//通过mybatis-plus自带的分页查询方法进行分页查询
Page<Furniture> page = new Page(pageNum, pageSize);
Page<Furniture> furniturePage = furnitureService.page(page);
return Result.success(furniturePage);
}
- 带条件的分页查询
- 创建分页模型
- 创建条件查询器
- 加入条件
- 进行查询
@GetMapping("/furniture/list/condition")
public Result listFurnitureByCondition(@RequestParam(value = "pageSize",defaultValue = "5") Integer pageSize,@RequestParam(value = "pageNum",defaultValue = "1")Integer pageNum,@RequestParam(value = "name",defaultValue = "")String name){
//使用mybatis-plus自带的条件查询
Page<Furniture> page = new Page(pageNum, pageSize);
//创建查询条件构造器
QueryWrapper<Furniture> wrapper = new QueryWrapper<>();
//按名字模糊查询
QueryWrapper<Furniture> queryWrapper = wrapper.like("name", name);
//进行查询
Page<Furniture> furniturePage = furnitureService.page(page, queryWrapper);
return Result.success(furniturePage);
}
mybatisPlus字段自动填充Handler
- 实现MetaObjectHandler接口,写一个自己的字段填充Handler
- 加上Component注解,注入spring容器
- 注意需要在对应的实体类上指定要自动填充的时机,不然不会生效
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Integer userId = null;
try {
userId = SecurityUtils.getUserId();
} catch (Exception e) {
e.printStackTrace();
userId = -1;//表示是自己创建
}
System.out.println("插入拦截器触发");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("createBy",userId , metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
this.setFieldValByName("updateBy", userId, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
try {
this.setFieldValByName("updateBy", SecurityUtils.getUserId(), metaObject);
} catch (Exception e) {
this.setFieldValByName("updateBy", -1, metaObject);
//e.printStackTrace();
}
}
}
- 在对应实体上的字段加上@TableField注解
- 通过注解的fill属性指定什么情况下进行填充,使用FieldFill的枚举值
- FieldFill.INSERT:插入数据时,进行字段填充
- FieldFill.UPDATE:修改数据时,进行字段填充
- FieldFill.INSERT_UPDATE:插入或修改数据时,进行字段填充
- FieldFill.DELETE:删除数据时,进行字段填充
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_user")
public class User implements Serializable {
//主键
@TableId(type = IdType.AUTO)
private Integer userId;
//用户名
private String userName;
//账号
private String userAccount;
//密码
private String userPassword;
//用户角色
private String userRole;
//状态
private String userState;
@TableField(fill = FieldFill.INSERT)
private Long createBy;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
}