目录
注:默认只扫描主程序类所在的包及其子包。要扫描其他包就要用:
@ComponentScan(”com.itany.controller”) //扫描组件
YAML最最基础语法_行_之_的博客-CSDN博客_yaml语法
如果配置文件中yml和properties都在,这两个文件都会自动生效,但是如果存在同样的配置冲突,则properties优先级更高。但是一般不会在一个项目里面放置两个配置文件。
(如果properties文件中,有值,还是会先加载properties中的值。)
5.2 加载spring配置文件 使用@ImportResoruce加载外部的Spring的XML文件,也是数组。
问题:如果确实需要将配置写在Spring的xml配置文件中,想加载xml文件怎么办?
例如:在实际的spring的开发过程中,Bean的添加是强烈不建议通过配置文件的方式来写配置信息的。
如果确实想要把Address这个Bean加到容器中去,那么建议用全注解的方式向容器中添加组件。
5.3 推荐使用注解方式添加组件 使用@Configuration和@Bean
"classpath:/METAINF/resources/"
实际上还有一个:"classpath:/",直接放在类路径下。但在springboot2.0中已经被废除,需要自己配置才能使用。
现在jar17之后,改成:spring.web.resources.static-locations=
搜索Thymeleaf官网,进入:在线阅读可以翻译成中文,下载文档是英文。
2.4 修改页面后,让其立即生效 + 热部署由于thymeleaf默认启用了缓存,所以修改html页面并不会实时生效。因此要在配置文件中禁用缓存。
3.1.1 th:text、th:utext :设置元素中的文本内容。区别:
3.1.2 th:html中的原生属性 :用来替换指定的html原生属性的值。
3.1.3 th:if、th:unless、th:switch和th:case条件判断,类似于(jstl里面的)c:if。
3.1.5 th:object、th:field 用于表单数据对象的绑定,将表单绑定到Controller的一个JavaBean参数,常与th:field一起使用,需要和*{}选择表达式配合使用。
3.1.6 th:fragment 声明代码片段,常用于页面头部和尾部的引入。(就是网站里每个页面中相同的导航部分和菜单栏,我们可以把它放到一个独立的页面中,然后在其他页面进行引入)
本文使用工具IDEA2022.3,JDK17,MySQL使用Navicat。
SpringBoot2.7.4
SpringBoot入门
一、SpringBoot简介
SpringBoot官网 https://spring.io/projects/spring-boot#overview
1. SpringBoot是什么?
产生背景:
- Spring开发变的越来越笨重,大量的XML文件,繁琐的配置,复杂的部署流程,整合第三方技术时 难度大等,导致开发效率低下。
- SpringBoot是一个用来简化Spring应用的初始化创建和开发的框架,简化配置,实现快速开发。
- 整合了整个Spring技术栈,JavaEE开发的一站式解决方案。
2. 为什么使用SpringBoot
优点:
- 快速创建独立运行的Spring应用并与主流框架集成 。
- 内置Servlet容器 (Tomcat、Jetty 或 Undertow) ,应用无需打包war包 。
- 使用starter(启动器)管理依赖并进行版本控制 。
- 大量的自动配置 (尽可能自动配置 Spring 和 3rd 方库) ,简化开发 。
- 提供了准生产环境的运行时监控,如 指标、 健康检查、外部配置等 。
- 无需配置 XML,没有生成冗余代码,开箱即用 。
二、第一个SpringBoot应用
1. 简介
推荐环境:
- SpringBoot 2.0(基于Spring5.0)
- JDK 1.8及以上
- Maven 3.2及以上
- Tomcat 8.5及以上
2. 步骤:
1. 创建一个maven的java工程
- 传统Web应用需要创建一个web工程,后期要打成war包,然后放到tomcat中 。
- springboot应用只需要创建一个java工程,后期直接打成jar包,其内置tomcat 。
2. 导入SpringBoot相关依赖
<! Inherit defaults from Spring Boot >
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<! Add typical dependencies for a web application >
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3. 编写Controller
4. 编写主程序类
注:默认只扫描主程序类所在的包及其子包。要扫描其他包就要用:
@ComponentScan(”com.itany.controller”) //扫描组件
@ComponentScan(”com.itany.controller”) //扫描组件
@SpringBootApplication //使用 @SpringBootApplication 标注主程序类,表示这是一个SpringBoot应用
public class MainApplication {
public static void main(String[] args) {
//启动SpringBoot应用
SpringApplication.run(MainApplication.class, args); //传入主程序类的Class对象
}
}
5. 部署打包
pom.xml中添加 插件 spring-boot-maven-plugin ,将应用打包成可独立执行的jar,(不必依靠IDEA)。
在cmd.exe 执行java -jar springboot01-helloworld-1.0-SNAPSHOT.jar ,执行前先要关闭IDEA的端口。
<! 该插件可以将应用打包 成 一个可执行的jar包 >
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
三、分析HelloWorld文件
1.1 POM
1.父项目是spring-boot-starter-parent。
2.通过启动器starter添加依赖。spring-boot-starter-web
3.加入spring-boot-maven-plugin插件,可以把应用打包成一个可执行的jar包。
(spring-boot-starter是最核心的基础的Starter,SpringBoot都需要这个Starter。spring-boot-starter-web这个依赖点进去,可以看到这个核心依赖spring-boot-starter,还有包含的其他依赖。)
详情如下:
-
1.父项目是spring-boot-starter-parent。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
- 1.1 父项目的父项目是spring-boot-dependencies,用来管理SpringBoot应用中依赖的版本,来进行进行版本控制。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath>../../springbootdependencies</relativePath>
</parent>
(spring-boot-dependencies里面有很多依赖的版本信息。如下图中的一部分:)
如果你用的到jar包版本和这里的不一个样,需要自己引入依赖。
如果你用到的jar包这里没有,那也需要自己引入。
- 2.通过启动器starter添加依赖。spring-boot-starter-web
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
(spring-boot-starter是最核心的基础的Starter,SpringBoot都需要这个Starter。spring-boot-starter-web这个依赖点进去,可以看到这个核心依赖spring-boot-starter,还有包含的其他依赖。)
还可以再这里看到所有已经被包含的依赖:
SpringBoot提供了许多Starter(启动器),分别对应不同中的应用场景,只要在项目中引入这些starter,相应场景的依赖就都会被导入。(就像spring-boot-starter-web里面包含了所有web场景所需要的所有依赖。)
在这个网址找(SpringBoot提供的Starter(启动器)):Developing with Spring Boot
https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
- 3.加入spring-boot-maven-plugin插件,可以把应用打包成一个可执行的jar包。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</dependency>
</dependencies>
1.2 主程序类
1.@SpringBootApplication标注在类上,表示这个类是SpringBoot的主配置类(主程序类,就通过该类的Main方法来启动SpringBoot应用)。@SpringBootApplication这个注解里面包含:
(如上图)@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。
- 1.1@SpringBootConfiguration标注在类上,表示这个类是SpringBoot的配置类(配置类可以有很多)。
@SpringBootConfiguration里面包含@Configuration(如上图), @Configuration标注在类上,表示这个类是Spring的配置类,相当于xml配置文件。
@Configuration里面包含@Component,@Component表示这个类是一个Spring的配置类组件。
- 1.2@EnableAutoConfiguration开启自动配置功能,SpringBoot会自动完成许多配置,简化了以前的繁琐的配置。
SpringBoot在启动时,会在类路径下的/META-INF/spring.factories中获取EnableAutoConfiguration指定的值,会将这些值作为自动配置类添加到容器中,这些自动配置类会帮我们完成许多配置工作。
- 1.3@ComponentScan标注在类上,指定要扫描的包(默认只扫描主程序类所在的包及其子包)(如果你要扫描的包不在这个范围内,就要加上这个注解。)。
四、快速创建SpringBoot项目
1. 简介
使用向导快速创建SpringBoot项目
使用 Spring Initializr 快速创建SpringBoot项目:
打开pom.xml可以看到:最关键的三个依赖已经被加入进去了(
1.父项目是spring-boot-starter-parent。
2.通过启动器starter添加依赖。spring-boot-starter-web
3.加入spring-boot-maven-plugin插件,可以把应用打包成一个可执行的jar包。
)。
spring-boot-starter-test是测试依赖。(使用包括 JUnit Jupiter、Hamcrest 和 Mockito 在内的库来测试 Spring Boot 应用程序的 Starter)
2. 基本操作
默认生成的.mvn、.gitignore、mvnw、mvnw.cmd等可以删除。
POM文件和主程序类都已经生成好了,直接写业务逻辑即可。
resources文件夹的目录结构:
|resources
|static 存放静态资源,如css、js、images
|templates 存放模板页面,可以使用模板引擎,如freemarker、velocity、theamleaf等
|application.properties SpringBoot应用的配置文件,可以修改一些默认配置。
Thymeleaf基本知识 - _freedom_yl - 博客园Thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模
https://www.cnblogs.com/dreamfree/p/4158557.html?utm_source=tuicool
配置文件
一、YAML的用法
1. 简介
SpringBoot的配置文件默认有两个:
- application.properties
- application.yml
上图是(springBoot2.0书写方法2018.09)。不同的springBoot版本书写方式有所不同,有的被重命名或者删除。具体参考:最新版(springBoot2.7.3书写方法2022.09:Common Application Properties)
然后就可以查找了:例如:
这两个配置文件的文件名固定,放在(resources目录下)classpath:/或classpath:/config目录下。可以通过配置文件修改SpringBoot的默认配置。
在写application.properties时:
2. YAML用法
YAML_360百科

2.1简介
- YAML不是一种标记语言;
- YAML是专门用来写配置文件的语言,以数据为中心,简洁且强大,比xml、properties更适合作为配置文件;
- YAML文件的后缀是.yml或.yaml。
2.2语法规则
- 大小写敏感;
- 使用缩进表示层级关系;
- 缩进时不允许使用Tag键,只允许使用空格;
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可;
- # 表示注释。
- 冒号后面要有空格。
# 修改默认配置
server:
port: 8882 # 写法key: value,冒号后面必须有空格
servlet:
context-path: /springboot03
2.3 基本用法
YAML支持的数据结构有三种:
- 字面量:单个值,不可再分的值。
- 对象:键值对的集合。
- 数组:一组按次序排列的值。
(数组(Array)是有序的元素序列。有序排列的同类数据元素的集合称为数组。数组是用于储存多个相同类型数据的集合。)
三种数据结构的用法:
1. 字面量:普通的值,字符串、数字、布尔等。
number: 25.5
str: hello
str: 'hello world'
#如果字符串包含空格或者特殊字符,则必须使用引号引起来,单引号或者双引号都可以。
name: 'tom cruise'
#“ 单引号'' ”不对特殊字符进行转义,会作为换行字符输出。结果为:tom 换行 cruise,\n 表示换行。
name: 'tom \n cruise'
#“ 双引号"" ”对特殊字符进行转义,会作为普通字符输出。结果为:tom \n cruise,\n 不表示换行。
name: "tom \n cruise"
flag: true
2. 对象,也称为Map映射,包含属性和值。
# 写法1:换行写,使用缩进
user:
name: tom
age: 21
sex: male
# 写法2:行内写法
user: {name: tom,age: 21,sex: male}
3. 数组,如List、Set等
# 写法1:换行写,使用 “短横线+空格+值”
(一组短横线开头的行,他们都属于上面这个属性(names)所对应的值)
names:
- tom
- jack
- alice
# 写法2:行内写法 逗号后面要有空格
names: [tom, jack, alice]
YAML最最基础语法_行_之_的博客-CSDN博客_yaml语法
3. 为属性注入值
通过加载配置文件,为类中的属性注入值。
过程:
新建一个User.java类,里面包含:
字面量类型username、age、status;
日期类型birthday;
包装类型address;
数组类型lists;
Map对象类型maps。
其中address需要新建一个Address.java类,包含province、city。
package com.itany.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
//必须将当前类添加到容器中
@Component
//默认读取全局配置文件获取值,将当前类中的所有属性与配置文件中的user进行绑定
@ConfigurationProperties(prefix = "user")
public class User{
// @Value("${user.username}")
private String username;
// @Value("${user.age}")
private Integer age;
// @Value("${user.status}")
private Boolean status;
// @Value("${user.birthday}")
private Date birthday;
// @Value不支持复杂类型的封装
private Address address ;
// @Value("${user.lists}")
private List<String> lists;
// @Value不支持复杂类型的封装
private Map<String,Object> maps;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public List<String> getLists() {
return lists;
}
public void setLists(List<String> lists) {
this.lists = lists;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
", status=" + status +
", birthday=" + birthday +
", address=" + address +
", lists=" + lists +
", maps=" + maps +
'}';
}
}
package com.itany.bean;
public class Address {
private String province;
private String city;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
// @Value不支持复杂类型的封装 private Address address ;
// @Value不支持复杂类型的封装 private Map<String,Object> maps;
在配置文件application.yaml中给这些对象注入值:还要配置端口号。
user:
username: tom
age: 25
status: true
birthday: 1997/05/08
address:
province: 河南
city: 洛阳
lists: [1, 2, 3]
#- tom
#- jack
#- alice
maps: { username: alice,age: 26,status: false }
或:
maps:
username: alice
age: 26
status: false
server:
servlet:
context-path: /springboot03
port: 8881
因为配置文件名称是固定的,所以写好之后,通过加载配置文件,就会为上述类中的对象注入值。
点开@ConfigurationProperties注解,显示:
只需要在类上加上@ConfigurationProperties注解,并且加上配置文件中的属性user作为前缀,绑定给user.java类。当做读取配置文件时获取user的值,就会绑定给user.java类。
必须将当前类添加到容器中去,所以还需要加上@Component注解。
//必须将当前类添加到容器中
@Component
//默认读取全局配置文件获取值,将当前类中的所有属性与配置文件中的user进行绑定
@ConfigurationProperties(prefix = "user")
public class User{
编辑Controller类,获取user值。
package com.itany.controller;
import com.itany.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
User user;
@RequestMapping("/hello")
public String hello(){
System.out.println(user);//控制台输出
return "Welcome to it.any!"+user;//页面输出
}
}
启动程序即可。
有这个爆红的“springBoot注解配置处理器没有找到”,可以加上: 编写配置文件就会有提示了。
<!--配置文件处理器,自动生成元数据信息,编写配置文件会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
假如控制台出现乱码:
在IDEA中,properties文件默认使用的编码格式是UTF-8,所以会有中文乱码。而properties文件本质上用的是ASCII编码。
如果还是不行,那就要把文件中的中文重新写一遍。
反正我的是怎么样都不行。还是乱码。
最后找到了一个IDEA2022控制台乱码的真正解决的办法:
因为:
控制台出现乱码,网上大部分的解决方案都是在file/setting/file encoding中全部设置为utf-8,或者在idea中修改VM文件在两个VM文件末尾加入-Dfile.encoding=UTF-8,但问题并没有解决。
看了很多文章,最后发现可能idea的控制台输入的编码和自己Windows的是一致的,所以不能盲目的都设置为utf-8,项目的编码要和系统的保持一致。
936是GBK编码。
视频中演示的例子:如下3.1
3.1 使用.yml配置文件
user:
username: admin
age: 18
status: true
birthday: 2018/2/14
address:
province: 江苏省
city: 南京市
lists:
list1
list2
list3
maps: {k1: v1,k2: v2}
// 将当前Bean添加到容器中
@Component
// 默认读取全局配置文件获取值,将当前类中的属性与配置文件中的user进行绑定
@ConfigurationProperties(prefix = "user")
public class User implements Serializable {
private String username;
private Integer age;
private Boolean status;
3.2 使用.properties文件
user.username=tom
user.age=21
user.status=false
user.birthday=2017/7/12
user.address.province=山东省
user.address.city=威海市
user.lists=list1,list2,list2
user.maps.k1=v1
user.maps.k2=v2
如果配置文件中yml和properties都在,这两个文件都会自动生效,但是如果存在同样的配置冲突,则properties优先级更高。但是一般不会在一个项目里面放置两个配置文件。
3.3 使用@Value为属性注入值
// 将当前Bean添加到容器中
@Component
// 默认读取全局配置文件获取值,将当前类中的属性与配置文件中的user进行绑定
// @ConfigurationProperties(prefix = "user")
public class User implements Serializable {
@Value("${user.username}")
private String username;
@Value("${user.age}")
private Integer age;
@Value("${user.status}")
private Boolean status;
@Value("${user.birthday}")
private Date birthday;
//@Value不支持复杂类型封装
private Address address;
@Value("${user.lists}")
private List<String> lists;
@Value和@ConfigurationProperties的比较:
@Value只能一个个为属性注入值,而@ConfigurationProperties可以批量为属性注入值;
@Value不支持复杂类型封装,而@ConfigurationProperties可以。
4. 多环境配置
可以为不同环境提供不同的配置信息,如开发环境、测试环境、生产环境。
多环境配置的两种方式:
- 创建多个properties文件。
- 创建一个yml文件,然后定义多个yml文档块。
4.1 创建多个properties文件
步骤:
1. 创建不同环境的properties文件
文件命名必须要命名为application-xxx.properties。
2. 在application.properties文件中指定要激活的配置。
# 指定激活的配置
spring.profiles.active=prod
指定激活的那个配置文件优先级最高,比默认的application.properties文件中的portserver.port=8081优先级高。
4.2 定义yml文档块
在yml文件中可以使用三个短横线“---”,定义多个文档块,表示不同的环境和配置。
1. yml中定义多个文档块,如下有三个。
spring:
profiles: develop
server:
port: 7771
---
spring:
profiles: testing
server:
port: 7772
---
spring:
profiles: product
server:
port: 8081
2. 在第一个文档块中指定要激活的配置testing。
#第一个文档块
server:
port:8881
# 指定要激活的配置 testing
spring:
profiles:
active: testing
---
spring:
profiles: develop
server:
port: 7771
---
spring:
profiles: testing
server:
port: 7772
---
spring:
profiles: product
server:
port: 8081
5. 加载外部的配置文件
单独创建一个user.properties文件放在resources下,把里面的值绑定给User。
问题:@ConfigurationProperties默认是从全局配置文件中获取值,如果想从外部的自定义属性文件中获取值怎么办?
解决:使用@PropertySources加载外部属性文件。这个注解是一个数组,意味着可以传递多个值。@PropertySource({"classpath:user.properties","classpath:userA.properties"})所以里面是一个大括号,可以用逗号隔开,然后写多个类路径的值。
5.1 加载properties属性文件
使用@PropertySource加载外部的属性文件。如下,在User类上加上
@PropertySource({"classpath:user.properties"})。
(如果properties文件中,有值,还是会先加载properties中的值。)
// 将当前Bean添加到容器中
@Component
// 加载外部的属性文件
@PropertySource({"classpath:user.properties"})
// 默认读取全局配置文件获取值,将当前类中的属性与配置文件中的user进行绑定
@ConfigurationProperties(prefix = "user")
public class User implements Serializable {
5.2 加载spring配置文件
使用@ImportResoruce加载外部的Spring的XML文件,也是数组。
// 加载外部的spring配置文件
@ImportResource({"classpath:spring.xml"})
@SpringBootApplication
public class Springboot03ConfigApplication {
问题:如果确实需要将配置写在Spring的xml配置文件中,想加载xml文件怎么办?
解决:使用 @ImportResource({"classpath:spring.xml"})注解 导入资源。
例如:在实际的spring的开发过程中,Bean的添加是强烈不建议通过配置文件的方式来写配置信息的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="address" class="com.itany.bean.Address">
<property name="province" value="山东"/>
<property name="city" value="济南"/>
</bean>
</beans>
在主程序中 使用 @ImportResource({"classpath:spring.xml"})注解 导入资源后,就能在Controller中使用@Autowired进行自动装配。否则是找不到Address的。
如果确实想要把Address这个Bean加到容器中去,那么建议用全注解的方式向容器中添加组件。
5.3 推荐使用注解方式添加组件
使用@Configuration和@Bean
// 标注在类上,表示这是一个配置文件,相当于以前的spring配置文件
@Configuration
public class SpringConfig {
// 标注在方法上,向容器中添加组件,将方法的返回值添加到到容器中,将方法名作为组件id
@Bean
public Address address(){
Address address = new Address();
address.setProvince("江苏");
address.setCity("苏州");
return address;
}
}
步骤:
主程序复原:
因为我们的Address是自己写的,所以可以直接在类上加上@Component。就加进容器中了。
但是如果我们遇到的类Bean是JDK或者其他地方的呢?就不能这样加入。
那就需要使用注解@Configuration和@Bean的方式添加组件。步骤:
配置文件不要了,我们通过一个类来替代这个配置文件。在包config下创建一个类(名字随意)springConfig.java。这个类用来替代xml文件,这个类上加上@Configuration就叫做配置类。
在这个类中定义一个方法,把要加入容器中的这个Bean,作为方法的返回值。方法上还要加注解@Bean。
这样就可以了。
因为:
@SpringBootApplication包含的 @ComponentScan标注在类上,指定要扫描的包(默认只扫描主程序类所在的包及其子包)(如果你要扫描的包不在这个范围内,就要加上这个注解。)。
所以:
我们加的配置类在Springboot03ConfigApplication.java这个主程序类所在的包com.itany的子包config中。
后期我们添加配置、组件的时候,就只通过配置类的形式去添加,不再去写配置文件了。
五、自动配置的原理
1. 执行过程
1. SpringBoot应用启动时会加载主程序类@SpringBootApplication,开启了自动配置功能@EnableAutoConfiguration。
2. @EnableAutoConfiguration作用:扫描所有jar包类路径下的METAINF/spring.factories文件,获取到EnableAutoConfiguration对应的值,将这些自动配置类添加容器中。
利用@Import({AutoConfigurationImportSelector.class})自动配置导入选择器,向容器中添加一些组件,也就是自动配置类。
查看AutoConfigurationImportSelector.class这个类中的selectImports()方法,
这个方法下面117行有一个叫做getCandidateConfigurations()获取候选的配置。里面有一个叫SpringFactoriesLoader.loadFactoryNames()的,
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
点进loadFactoryNames()方法,
查看里面的oadSpringFactories()方法,找到(List)loadSpringFactories()方法,
里面有一行:
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
证实:
SpringBoot应用启动时会加载主程序类@SpringBootApplication,开启了自动配置功能@EnableAutoConfiguration。
@EnableAutoConfiguration的作用:扫描所有jar包类路径下的META-INF/spring.factories文件,获取到EnableAutoConfiguration对应的值,将这些自动配置类添加容器中。
新版本的spring-boot-autoconfigure-2.7.1.jar的jar包中已经没有# Auto Configure自动配置了,
相比老版本的# Auto Configuration Import Filters,多了两行:Bean条件和Web应用条件。
详情如下:
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
3. 通过这些自动配置类完成相应的配置功能。
2. 原理分析
以HttpEncodingAutoConfiguration为例:
// 表示这是一个Spring配置类
@Configuration
// 启用HttpEncodingProperties类的ConfigurationProperties功能,通过配置文件为其属性注入值,并将其添加到容器中
@EnableConfigurationProperties({HttpEncodingProperties.class})
// 如果当前应用是Web应用,则该配置类生效,否则不生效
//servlrt:(尤指 Java 语言中在服务器上运行的)小型应用程序;小服务程序
@ConditionalOnWebApplication(
type = Type.SERVLET
)
// 如果当前应用中有CharacterEncodingFilter类-字符编码过滤器,则该配置类生效,否则不生效
@ConditionalOnClass({CharacterEncodingFilter.class})
// 如果配置文件中有spring.http.encoding.enabled选项,则该配置项生效,否则不生效,默认已经设置为True,所以默认生效
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
private final HttpEncodingProperties properties;
// 将容器中HttpEncodingProperties注入
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
// 将方法返回值添加到容器中
@Bean
// 如果容器中没有这个组件,则添加
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.aut
oconfigure.http.HttpEncodingProperties.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.au
toconfigure.http.HttpEncodingProperties.Type.RESPONSE));
return filter;
}
注意:
根据当前不同的条件判断,决定自动配置类是否生效,即并不是所有的自动配置类都会生效。
自动配置类xxAutoConfiguration的属性是从对应的 xxxProperties类中获取的。
xxxProperties类中的所有属性是通过配置文件注入绑定的。所以我们可以通过配置文件指定这些属性的值。
spring.http.encoding.charset=gbk
spring.http.encoding.force=true
总结:
SpringBoot启动时会加载大量的自动配置类(xxxAutoConfiguration) xxxProperties。
通过这些自动配置类向容器中添加组件。
通过这些组件来实现自动配置功能,简化配置。
如何确认哪些自动配置类生效了?哪些没有生效?
在配置文件中,开启deBug模式。
能看到环境评估报告、开启的匹配、未开启的匹配。
六、Web开发
1. 简介
使用SpringBoot开发Web应用的步骤:
1. 创建SpringBoot应用,选择相应的Starter。
2. 在配置文件中指定必要的少量配置;(例如数据库的密码、地址)。
3. 编写业务代码。
Web开发的自动配置类:WebMvcAutoConfiguration
2. 静态资源的映射
2.1 静态资源位置
查看WebMvcAutoConfiguration.class——>addResourceHandlers()——>getStaticLocations()
——>staticLocations——>CLASSPATH_RESOURCE_LOCATIONS
= new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/","classpath:/public/"};
所以,静态资源的默认位置:
-
"classpath:/METAINF/resources/"
-
"classpath:/resources/"
-
"classpath:/static/"
-
"classpath:/public/"
-
实际上还有一个:"classpath:/",直接放在类路径下。但在springboot2.0中已经被废除,需要自己配置才能使用。
- 使用时,会按照这个顺序去寻找资源,因为源码中可以看到是一个String[]数组,所以是有一个从上到下的优先级的。
类路径就是这个resources文件夹。例如:"classpath:/static/"="resources:/static/"。我们一般常用的就是把静态资源css、js、images等都放在static文件夹下。
修改默认的静态资源的位置:
原来是这样的spring.resources.XXX,所以可以如下指定静态资源的位置:
# 指定静态资源的位置
spring.resources.staticlocations=classpath:/static,classpath:/public
现在jar17之后,改成:spring.web.resources.static-locations=
#指定静态资源的位置
spring.web.resources.static-locations=classpath:/static,classpath:/public
当你去访问静态资源时,会到上述的五个位置去访问, 例如访问图片:
http://localhost:8080/static/image/S.png这样写却是不对的。要直接写:http://localhost:8080/image/S.png
例如修改静态资源位置:
#指定静态资源的位置
spring.web.resources.static-locations=classpath:/static,classpath:/public
自定义修改静态资源的位置之后,默认的那些{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/","classpath:/public/"};就作废了。只能访问修改后的这两个路径classpath:/static,classpath:/public这两个文件夹下的资源。
详细图如下:
2.2 欢迎页
WebMvcAutoConfiguration.class--> welcomePageHandlerMapping()-->getWelcomePage()-->getStaticLocations()说明:
将index.html页面放到任意一个静态资源文件夹中即可。
示例创建一个欢迎页:得到
默认的欢迎页:http://localhost:8080
和能访问的页面:http://localhost:8080/index.html
2.3 图标
在老版的spring中, WebMvcAutoConfiguration.class-->内部类FaviconConfiguration-->faviconHandlerMapping可以看到:
但是在新版中,SpringBoot从2.1.X过渡到2.2.X后,网站图标favicon.ico是找不到的。把默认带的spring网站图标文件favicon.ico文件给删了,即不提供默认的ico文件了,并且把原本在autoconfig里面的配置删了。解释:favicon.ico即Favorites Icon的缩写,它是一个图标,会出现在支持它的浏览器标题左边。浏览器会打开网站会自动在网页项目的资源目录下搜索这个文件名的图标文件,有就显示。故springboot即我们的项目其实现在是不需要过多的配置的。我想作者也是想精简项目代码吧,不做重复的操作。
如上新版的解释:但是描述的也不准确。仅供参考。
<link rel="icon" href="assets/img/favicon.ico">
但是其实,就像下文描述的那样,可以直接在资源目录下创建一个favicon.ico,浏览器就能自动识别并使用。
所以我们依然可以使用:
必须将图片命名为favicon.ico放到任意一个静态资源文件夹中。但是必须用外部访问地址,通过home+r键的到本台主机IPv4的地址+port(s): 8080 (http)端口号,如图:192.168.1.197:8080/就可以看到访问的地址带上了图标了。内部访问地址:http://localhost:8080是看不到图标的。
七、模板引擎
1. 简介
目前Java Web开发推荐使用模板引擎,不建议使用JSP页面。
- JSP缺点:本质上就是Servlet,需要后台编译,耗时,效率低。
- 模板引擎:不需要编译,速度快。
常见的模板引擎:Freemarker、Velocity、Thymeleaf等
SpringBoot推荐使用Thymeleaf,且默认不支持JSP,因为JSP必须要打包war包才行。
补充:目前主流Web开发更推荐采用前后端分离的形式,前端使用MVVM框架:Vue.js、Angular、React等。
2. 使用步骤
步骤:
找到依赖,并添加:

2.1 添加Thymeleaf的依赖
<!--添加Thymeleaf相关的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2. 2将HTML页面放到templates目录中
只要把html页面放到templates文件夹下面,thymeleaf就能够自动渲染。
但是templates目录下的HTML页面默认不能被直接访问(因为静态资源里面没有这个位置),需要通过controller来访问,由thymeleaf来渲染,自动添加前缀和后缀。
看看源码:搜索ThymeleafAutoConfiguration.class-->ThymeleafProperties.class
默认自动去添加HTML页面的前缀和后缀。
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
示例:return"success"被自动田间前缀和后缀,就相当于:
return“classpath:/templates/success.html”。
假如页面中我有数据需要渲染并展示:
@Controller
public class TemplateController {
@RequestMapping("/test1")
public String test1(Model model){
model.addAttribute("name","alice");
return "success"; //自动添加前缀/templates和后缀.html
}
}
那么我们怎么使得html的Thymeleaf像jsp页面一样可以使用el表达式、jstl等?Thymeleaf怎么取得页面上的数据呢?请看3.使用Thymeleaf
搜索Thymeleaf官网,进入:在线阅读可以翻译成中文,下载文档是英文。
教程:使用 Thymeleaf
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#using-texts
仔细看看这个文档吧!!!!!!!!!!!!
2.3 使用Thymeleaf
通过参考手册,我们知道首先需要给html加上下面这一行:引入Thymeleaf命名空间,然后利用Thymeleaf所提供的属性。
<html xmlns:th="http://www.thymeleaf.org">
然后这些非标准属性获取就合法了。
<!DOCTYPE html>
<!导入thymeleaf的命名空间>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF8">
<title>Title</title>
</head>
<body>
<h2>success</h2>
<! 使用th:text属性,设置标签中的文本,表达式${}可以获取模型中的数据 >
<div th:text="${name}"></div>
</body>
</html>
2.4 修改页面后,让其立即生效 + 热部署
由于thymeleaf默认启用了缓存,所以修改html页面并不会实时生效。因此要在配置文件中禁用缓存。
# 禁用thymeleaf的缓存
spring.thymeleaf.cache=false
补充:还需要开启IDEA的自动编译,IDEA默认是不自动编译
Settting——>搜索Compiler——>Build project automatically,勾选,并应用。已停止的会自动编译,正在运行的项目是不会自动编译的。
还需要下面这个:
Help——>Find Action——>搜索Registry——>勾选compiler.automake.allow.when.app.running(现在这个选项已经被迁移了)。这个勾选之后,正在运行的项目会自动编译。
由于这个已经被迁移,所以可以看到这里已经找不到了:Ctrl+Alt+Shift+/ 可以出现这个Maintenance页面选择Registry。
但是,在IDEA2020.1.3中,这一样设置,还是可以使用热部署的。 重新启动项目后,再修改js,html,css就不用重启工程啦!
IDEA2022.1.3却是不能这样进行热部署了。compiler.automake.allow.when.app.running选项在IntelliJ IDEA 2021.2之后的版本迁移到高级设置中:
重新启动项目后,再修改java代码、js,html,css就不用重启工程啦!好赞哦!!!!!!修改java代码里的类都直接热部署!!!
每次修改java代码,点击刷新页面就会自动初始化重新运行项目如下,然后才能看到修改结果。但是修改js,html,css不会去初始化,而是热部署,直接刷新就看到了结果。
3. 语法规则
3.1 常用属性
3.1.1 th:text、th:utext :设置元素中的文本内容。区别:
th:text对特殊字符进行转义,等价于内联方式[[${ }]]。结果出来还是特殊字符。
th:utext对特殊字符不进行转义,等价于内联方式[(${ })]。结果出来变成 特殊字符起作用后的效果。 <mark>会使得背景标黄。
而且属性是默认替换标签中的东西。也就是说,在div中写的内容aaa、bbb并不会被显示出来而是只能显示出标签<div th:text="${hello}">的内容值。 那怎么让浏览器识别呢?用内联的方式,可以在文本前后添加自定义的内容。
3.1.2 th:html中的原生属性 :用来替换指定的html原生属性的值。
使用前:显示的是html原生属性的值。
使用后:显示的是 被替换的指定的html原生属性的值。
3.1.3 th:if、th:unless、th:switch和th:case条件判断,类似于(jstl里面的)c:if。
th:if
还有一个否定对应物:th:unless。
age不是小于18岁(unless age<=18),就显示:已成年。
th:switch:<!--默认:如果都不匹配,会选择显示th:case="*"所代表的内容-->
3.1.4 th:each循环,类似于c:forEach。
假如名字有什么后缀资源:
3.1.5 th:object、th:field 用于表单数据对象的绑定,将表单绑定到Controller的一个JavaBean参数,常与th:field一起使用,需要和*{}选择表达式配合使用。
3.1.6 th:fragment 声明代码片段,常用于页面头部和尾部的引入。(就是网站里每个页面中相同的导航部分和菜单栏,我们可以把它放到一个独立的页面中,然后在其他页面进行引入)
需要用下面的进行3.1.7 th:include、th:insert、th:replace引入代码片段。自动添加前缀、后缀,所以不用写include/header.html::head。
详细示例:
3.1.7 th:include、th:insert、th:replace引入代码片段,类似于jsp:include。
三者的区别:
th:include 保留自己的标签,不要th:frament的标签(Thymeleaf 3.0中已经不推荐使用 th:include )。
th:insert 保留自己的标签,保留th:frament的标签。里面还有<div>、<header>。
th:replace 不要自己的标签,保留th:frament的标签。里面没有<div>,还有<header>。
还可以:
尝试:记得加#。
3.2 表达式
${} 变量表达式获取对象的属性、方法。
使用内置的基本对象,如session、application等。
使用内置的工具对象,如#strings、#dates、#arrays、#lists、#maps等。
*{} 选择表达式(星号表达式),需要和th:object配合使用,简化获取对象的属性。如下,不用每一个属性都写user.XXX。
<!--*{} 选择表达式(星号表达式) 简化后:-->
<div th:object="${user}">
<div th:text="*{id}"></div>
<div th:text="*{name}"></div>
<div th:text="*{age}"></div>
</div>
<!--简化前:-->
<div th:text="${user.id}"></div>
<div th:text="${user.name}"></div>
<div th:text="${user.age}"></div>
@{} url表达式,定义url。
查询指定的用户信息,使用th:href="@{/findUser(username=${user.name})}",使得用户信息是一个可以改变的值${user.name},而不是写死的tom。注意:<script th:src="@{/js/common.js}"></script><link rel="stylesheet" th:href="@{/css/style.css}">
<!DOCTYPE html>
<!--导入thymeleaf的命名空间-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>
<!--@{} url表达式-->
<a href="" th:href="@{/findUser(username=${user.name})}">查询指定的用户信息</a>
<a href="prodect/list.html" th:href="@{/prodect/list}">商品列表</a>
<script th:src="@{/js/common.js}"></script>
</body>
</html>
运算符:
算术运算:+
、-
、*
和。/ % ,
其中一些运算符存在文本别名:( div
) /
、mod
( %
)。
文本别名的比较符:( gt
) >
、lt
( <
)、ge
( >=
)、le
( <=
)、not
( !
)。还有eq
( ==
), neq
/ ne
( !=
)。
不是别名:页面中只能写and、or:
WW>=10 & XX>=15(页面中只能写and:WW>=10 and XX>=15)
WW>=10 || XX>=15(页面中只能写or:WW>=10 or XX>=15)
<input type="radio" name="age"
th:checked="${session.age} > '60' or (${session.age} < '100' and ${session.age} >'50')">老年
<div th:text="${user.age>=2?'大于等于2':'小于2'}"></div>
4. 热部署
2022.1IDEA可以不必会这样加这个依赖,就能热部署。具体参考上面的(七、2.4 修改页面后,让其立即生效)
使用SpringBoot提供的devtools实现热部署(更改java代码也不需要重启)。
原理:实现监控classpath下文件的变化,如果发生变化则自动重启。
配置:添加devtools依赖。
<!devtools>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!该依赖不传递>
<optional>true</optional>
</dependency>
八、扩展默认的SpringMVC功能
1. 简介
以前在SpringMVC中通过如下代码在XML文件中配置,来实现视图跳转和拦截器:
视图跳转:
<mvc:viewcontroller path="/showLogin" viewname="login"/>
拦截器:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean class="com.itany.interceptor.HelloInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
SpringBoot自动配置默认并没有提供以上功能配置,需要自己扩展,使用WebMvcConfigure接口。
2. 基本操作
直接访问templates里面的页面,默认是访问不了的(只能通过Controller进行跳转):
2.1视图跳转:
在config文件夹里创建类CustomMvcConfig,然后如下
2.2拦截器:
2.2.1.在interceptor文件夹里创建拦截器类MyInterceptor,实现HandlerInterceptor接口(接口里面的方法也是都加了default,默认实现)。
2.2.2.实现HandlerInterceptor接口里面的方法:
在SpringBoot中我们可以使用HandlerInterceptor(HandlerInterceptorAdapter拦截器适配器已经作废)这个拦截器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。
在HandlerInterceptor中主要提供了以下的方法:
- boolean preHandle():在访问controller方法之前执行,在该方法中可以做类似校验的功能。返回为true才会去执行Controller方法,继续调用下一个拦截器。返回false,则中断执行,也就是说我们想调用的方法不会被执行,但是你可以修改response为你想要的响应。被拦截了,原路打回(主要做权限控制,有权限才放行)。
- void postHandle():在执行controller方法之后, 执行jsp页面之前执行该方法,可以向作用域中放入数据,影响jsp展示效果,(可以在执行jsp之前做渲染)。
- void afterCompletion:在jsp页面渲染完成之后或者调用方已经拿到响应,整个请求处理完毕后进行回调,执行。(主要用于记录日志,资源释放。)
。 - 【注意小知识点来啦】如果preHadle返回true,但是没有找到对应的Controller,是不会执行postHandle方法哦。
可以自己去看解释:
具体代码如下:
package com.springbootstudy.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("MyInterceptor.preHandle");//执行业务方法之前
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");//业务方法执行完之后
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");//视图渲染之后
}
}
最新版的里面没有return false,改为了如下一行, 就不用把return false;改成return true;。
return HandlerInterceptor.super.preHandle(request, response, handler);
- request:在该参数中可以获取到和请求相关的信息。比如是否为get请求等。
- response:在该参数中可以获取对象的响应信息。
- handler:该参数中包含了对应方法的信息。比如:方法中的参数类型、参数的注解、方法的注解等信息。
package com.irany.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override//执行业务方法之前
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("MyInterceptor.preHandle");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override//业务方法执行完
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override//视图渲染之后
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2.2.3.稍加修改拦截器里被实现的方法的代码如下图:
2.2.4.返回到config文件夹里的CustomMvcConfig类中,ctrl+o选择重写方法addInterceptors()添加拦截器:
//添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册.添加拦截器(已经写好的拦截器类 新建MyInterceptor).添加拦截的url路径.去除不需要拦截的路径
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/showLogin");
// /**表示拦截所有请求
}
被拦截的访问,控制台会输出拦截器里的输出内容:
没有被拦截的访问,控制台是空白,说明没有经过拦截器:
3.步骤:
3.1. 定义一个配置类CustomMvcConfig,实现WebMvcConfigure接口。
3.2. 根据需要实现相应的方法。视图跳转addViewControllers和拦截器addInterceptors。
/**
* Description:扩展默认的SpringMVC功能
* 要求:
* 1.使用@Configuration标注为配置类
* 2.实现WebMvcConfigurer接口
* 3.根据需要实现相应的方法
* 注:这个接口中的方法都添加了Jdk1.8中的default方法修饰,不强制实现所有的方法(jdk1.8新
特性)
* 在SpringBoot1.0中是继承WebMvcConfigurerAdapter类,在SpringBoot2.0中已过时
*/
@Configuration
public class CustomMvcConfig implements WebMvcConfigurer {
//添加ViewController,实现视图跳转。(ViewControllerRegistry 视图控制器注册)
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//访问/showLogin时跳转到login.html视图
registry.addViewController("/showLogin").setViewName("login");
}
//添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册.添加拦截器(已经写好的拦截器类 新建MyInterceptor).添加拦截的url路径.去除不需要拦截的路径
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/showLogin");
// /**表示拦截所有请求
}
}
4.WebMvcConfigure接口里面有很多方法,ctrl+o就可以看到如下:
这些方法都以default开头,表示这些方法都是默认实现的。现在就表明。不必在继承了WebMvcConfigure接口的方法里面实现这个接口的所有方法了。
九、全局异常处理
1. 简介
当程序出现异常时进行全局处理,SpringBoot默认的异常提示: Whitelabel Error Page,如下:
如果真的有异常,我们要给一个更友好的提示,有两种方式:
- 定义错误码页面
- 定义异常通知
2. 定义错误码页面
创建 错误状态码.html 页面(例如:404.html、500.html),放在templates/error目录中,当发生错误时会自动到该目录下查找对应的错误页面。
在templates文件夹里创建一个新文件夹error,然后在里面创建如 4xx.html 或 5xx.html 页面,用来匹配所有该类型的错误(会先进行精确匹配(有404.html 就用),然后模糊匹配(没有404.html ,就用 4xx.html)。)。自动就匹配进入这个错误页面了。
在例如:手动写一些错误异常
如果我们,删除500.html,就会匹配5xx.html。
<h2>5xx错误</h2>
<h3>状态码:[[${status}]]</h3>
<h3>错误提示:[[${error}]]</h3>
<h3>异常消息:[[${message}]]</h3>
<h3>时间戳:[[${timestamp}]]</h3>
3. 定义异常通知
在Controller文件夹下新建类ExceptionAdvice。定义异常通知 ,用来处理全局异常。例如算术异常:
详细代码:
package com.springbootstudy.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ExceptionAdvice {//异常通知 ,用来处理全局异常。
@ExceptionHandler(ArithmeticException.class)
public String arithmetic(Exception e){// 处理 Arithmetic算术Exception异常
System.out.println("警报,程序出现异常,发短信:"+e.getMessage());
return "error/5xx";
}
@ExceptionHandler(Exception.class)
public String exception(Exception e){//其它的异常
System.out.println("警报,程序出现异常,发邮件:"+e.getMessage());
return "error/5xx";
}
}
其他异常:
十、关于Servlet容器
1. 简介
SpringBoot中默认内置了Servlet容器:Tomcat。
问题:SpringBoot默认是以jar包的方式启动内置的Servlet容器,没有web.xml文件,如何注册Servlet三大组件:Servlet(控制器)、Filter(解决乱码的过滤器)、Listener(监听器,监听特定的事件)?
解决:通过自定义Servlet配置,通过servlet提供的API(应用程序接口Application Programming Interface )来注册组件,使用ServletRegistrationBean、FilterRegistrationBean、
ListenerRegistrationBean。
2. 注册Servlet组件
步骤:
2.1 Servlet(控制器)
1. 在servlet包下面,定义一个配置类MyServlet,继承组件extends HttpServlet。然后重写里面的doGet()、doPost()方法,并修改。
改完得到:
package com.irany.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Myservlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("Myservlet.doGet");
//super.doGet(req, resp);
doPost(req, resp);//调用下面的 doPost()方法
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("Myservlet.doPost");
//super.doPost(req, resp);
}
}
2. 在config文件夹下自定义一个CustomServletConfig方法,加注解@Configuration,用来注册组件。
创建实例new ServletRegistrationBean<>(),方法:
得到:
//创建实例new ServletRegistrationBean<>(),
ServletRegistrationBean<Myservlet> registrationBean
= new ServletRegistrationBean<>();
完整代码:
package com.irany.config;
import com.irany.servlet.Myservlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Servlet;
@Configuration //自定义的Servlet配置
public class CustomServletConfig {
/*注册Servlet:
* 使用API组件:ServletRegistrationBean。
* 方法名可以随意,就叫myServlet(),方法上加上@Bean。
* */
@Bean
public ServletRegistrationBean myServlet(){
//创建实例new ServletRegistrationBean<>(),控住器注册组件
ServletRegistrationBean<Myservlet> registrationBean
= new ServletRegistrationBean<>();
//把需要注册的Myservlet()传进 注册组件.setServlet设置控制器
registrationBean.setServlet(new Myservlet());
//注册组件.添加组件Servlet路径映射(一个或多个映射)。通过这个映射来访问组件Servlet。
registrationBean.addUrlMappings("/myServlet");
//返回注册组件
return registrationBean;
}
}
得到结果:
2.2 Filter(解决乱码的过滤器)
在com.itany文件夹中新建一个的filter文件夹,新建一个类MyFilter,实现接口implements Filter。实现三个方法:init()、doFilter()、destroy()。
得到如下代码:
package com.irany.filter;
import javax.servlet.*;
import java.io.IOException;
public class MyFilter implements Filter {
@Override //初始化
public void init(FilterConfig filterConfig)
throws ServletException {
Filter.super.init(filterConfig);}
@Override //执行过滤器
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain)
throws IOException, ServletException {
System.out.println("MyFilter.doFilter");
//调用 过滤器链.执行过滤器(请求,响应)
filterChain.doFilter(servletRequest,servletResponse);
}
@Override //销毁的时候
public void destroy() {
Filter.super.destroy();
}
}
返回到com.irany.config文件夹中的CustomServletConfig类里,注册Filter,使用API组件:FilterRegistrationBean。
注:得到步骤4这一行代码的写法如下图:
具体代码如下:
/*注册Filter:
* 使用API组件:FilterRegistrationBean。
* 方法名可以随意,就叫myFilter(),方法上加上@Bean。
* */
@Bean
public FilterRegistrationBean myFilter(){
//创建实例new FilterRegistrationBean<>(),过滤器注册组件
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
//注册组件.设置filter
registrationBean.setFilter(new MyFilter());
//注册组件.添加需要过滤的路径映射(数组)(一个或多个映射)。通过这个映射来过滤。
registrationBean.addUrlPatterns("/showLogin");
registrationBean.addUrlPatterns("/test1");
//返回注册组件
return registrationBean;
}
现在不让这样写一组("/showLogin","test1"),只能分多次添加,才能识别。
//注册组件.添加需要过滤的路径映射(数组)(一个或多个映射)。通过这个映射来访问。 registrationBean.addUrlPatterns("/showLogin","test1");
要写成:
//注册组件.添加需要过滤的路径映射(数组)(一个或多个映射)。通过这个映射来访问。
registrationBean.addUrlPatterns("/showLogin");
registrationBean.addUrlPatterns("/test1");
得到结果:
2.3 listener监听器,监听特定的事件。
根据要监听的事件不同去实现不同的接口。比如我要监听session建立的时候?还是监听session销毁的时候?还是Web应用启动的时候?
下面以“在Web应用启动时进行监听”为例,实现implements ServletContextListener接口(Servlet上下文侦听器)。
在com.itany文件夹中新建一个的listener文件夹,新建一个类MyListener,实现接口implements Filter。实现contextInitialized()、contextDestroyed()方法。
具体代码:自己加上输出语句。
package com.irany.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyListener implements ServletContextListener {
@Override //web应用启动的时候
public void contextInitialized(ServletContextEvent sce) {
System.out.println("MyListener.contextInitialized");
ServletContextListener.super.contextInitialized(sce);
}
@Override //web应用销毁的时候
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("MyListener.contextDestroyed");
ServletContextListener.super.contextDestroyed(sce);
}
}
然后返回到com.irany.config文件夹中的CustomServletConfig类里,注册监听器,使用API组件:ServletListenerRegistrationBean。
/*注册Litener:
* 使用API组件:ServletListenerRegistrationBean。
* 方法名可以随意,就叫myListener(),方法上加上@Bean。
* */
@Bean
public ServletListenerRegistrationBean myListener(){
//创建实例new ServletListenerRegistrationBean<>(),Web监听器注册组件
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>();
//注册组件.设置Listener
registrationBean.setListener(new MyListener());
//返回注册组件
return registrationBean;
}
启动Web应用时,可以看到:
点击这个按钮Exit,销毁。
2.4 三大组件代码:
package com.irany.config;
import com.irany.filter.MyFilter;
import com.irany.listener.MyListener;
import com.irany.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;
@Configuration //自定义的Servlet配置
public class CustomServletConfig {
/*注册Servlet:
* 使用API组件:ServletRegistrationBean。
* 方法名可以随意,就叫myServlet(),方法上加上@Bean。
* */
@Bean
public ServletRegistrationBean myServlet(){
//创建实例new ServletRegistrationBean<>(),控住器注册组件
ServletRegistrationBean<Myservlet> registrationBean = new ServletRegistrationBean<>();
//把需要注册的Myservlet()传进 注册组件.setServlet设置控制器
registrationBean.setServlet(new Myservlet());
//注册组件.添加组件Servlet的路径映射(数组)(一个或多个映射)。通过这个映射来访问Servlet。
registrationBean.addUrlMappings("/myServlet");
//返回注册组件
return registrationBean;
}
/*注册Filter:
* 使用API组件:FilterRegistrationBean。
* 方法名可以随意,就叫myFilter(),方法上加上@Bean。
* */
@Bean
public FilterRegistrationBean myFilter(){
//创建实例new FilterRegistrationBean<>(),过滤器注册组件
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
//注册组件.设置filter
registrationBean.setFilter(new MyFilter());
//注册组件.添加需要过滤的路径映射(数组)(一个或多个映射)。通过这个映射来过滤。
registrationBean.addUrlPatterns("/showLogin");
registrationBean.addUrlPatterns("/test1");
//返回注册组件
return registrationBean;
}
/*注册Litener:
* 使用API组件:ServletListenerRegistrationBean。
* 方法名可以随意,就叫myListener(),方法上加上@Bean。
* */
@Bean
public ServletListenerRegistrationBean myListener(){
//创建实例new ServletListenerRegistrationBean<>(),Web监听器注册组件
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>();
//注册组件.设置Listener
registrationBean.setListener(new MyListener());
//返回注册组件
return registrationBean;
}
}
3. 使用外部的Servlet容器
3.1 内、外Servlet容器的优缺点
使用内置Servlet容器:将应用打成可执行的jar包,直接运行。
优点:简单、方便。
缺点:不支持JSP、可定制性差。
使用外部Servlet容器:将应用打成war包,然后部署到外部的Servlet容器中(例如Tomcat)。
优点:支持JSP、可定制性强。
3.2 操作步骤
步骤:
3.2.1. 创建一个Maven的war工程。
有如下三个变化:
3.2.1.1 打包方式为war。
<packaging>war</packaging>
3.2.1.2将内置Tomcat的scope(范围)配置为provided。(提供,但不打包到war包内部。)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
3.2.1.3除了主程序类Springboot05WarApplication之外,自动又定义了一个SpringBootServletInitializer的子类。
(并且已经写好了configure()配置方法,如下:通过sources()方法获得主程序类Springboot05WarApplication的内容,才能初始化)。
package com.itany;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/*要求:
* 1.继承SpringBootServletInitializer(springBoot程序初始化)类
* 2.重写configure()方法
* 3.调用SpringApplicationBuilder的sources()方法,
* 传入SpringBoot应用的主程序类
* */
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
//传入SpringBoot应用的主程序类 Springboot05WarApplication.class
return application.sources(Springboot05WarApplication.class);
}
}
3.2.2. 创建Web目录
File——>Project Structure(Ctrl+Alt+Shift+S)——>Modules——>Web Resource Descriptors(Web资源描述符)——>+
然后还需要创建web.xml文件(也叫部署描述符文件Deployment Descriptors)。
File——>Project Structure(Ctrl+Alt+Shift+S)——>Modules——>Deployment Descriptors——>+
点击这个web.xml出现下面的弹窗。修改路径,然后应用即可。
然后,就可以在webapp目录下面创建JSP页面了。那怎么访问这个JSP页面index.jsp呢?
需要打包package。出现报错,是包的版本不匹配。修改pom.xml文件中的这个包的17版本为1.8。
还要修改这里:
其他要修改的地方,参见:
(31条消息) 解决Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile_饭一碗的博客-CSDN博客
https://blog.csdn.net/fanrenxiang/article/details/80864908
最好是在创建文件的时候就改成和电脑中的JDK版本一致的1.8或者其他。这里默认是17。
在创建文件的时候就改成和电脑中的JDK版本一致的17.安装最新版本的JDK17。
打包成功之后,在target文件夹里就可以看到war包,把这个war包拷贝到外部的Tomcat中,就能运行了。
但是这样很麻烦,所以我们还是像以前一样,把Tomcat加到IDEA中。关于Tonmcat要注意,使用
Tomcat 8.5及以上的版本。
最好把默认浏览器也设置好。
然后,运行这个Tomcat9.0.就进入这个JSP页面了。
但是我们写自定义的Controller的时候,要访问的页面放在webapp\WEB-INF\views\xx.jsp页面时,会找不到。
出现以下错误,并且访问/hello在跳转时出现404,找不到success.jsp页面。报错:
因为默认是去resources\templates文件夹下面找的,所以需要我们指定静态资源的位置,如下。
spring.web.resources.static-locations=/webapp
并且在application.properties中配置前缀和后缀。这样可以帮我们找到webapp文件夹。
得到结果:
3.2.3. 配置前缀和后缀,并指定静态资源的位置。
spring.web.resources.static-locations=/webapp
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
3.2.4. 配置Tomcat
Tomcat 8.5及以上
十一、SpringBoot数据访问
1. JDBC
步骤:
1. 创建一个工程,选择以下模板:Web、MySQL、JDBC。然后在pom文件中就能看到。
如果你要使用数据源的话,可以把数据源的依赖也加过来,
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
2. 配置数据库连接信息
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=org.apache.commons.dbcp.BasicDataSource
#type表示使用的是哪一种数据源,是dbcp。
数据源的自动配置可以参考这个文件DataSourceAutoConfiguration.class里的信息:
要配置的地方都在这里:配置的时候,就可以参考这个文件里面的。
3. 测试:
方法一:建议这样做。
利用Navicat建一个数据库叫做springboot,里面建一个表叫t_user。
方法二:不建议,只说一点,不说完了。
打开mysql,进入到MySQL的bin目录中。
C:\Users\LENOVO>cd C:\Program Files\MySQL\MySQL Server 8.0\bin
输入:mysql -u root -p
C:\Program Files\MySQL\MySQL Server 8.0\bin>mysql -u root -p
出现Enter password:,输入密码。进入mysql>。
Microsoft Windows [版本 10.0.19044.2006]
(c) Microsoft Corporation。保留所有权利。
C:\Users\LENOVO>cd C:\Program Files\MySQL\MySQL Server 8.0\bin
C:\Program Files\MySQL\MySQL Server 8.0\bin>mysql -u root -p
mysql: [Warning] mysql: ignoring option '--no-beep' due to invalid value ''.
Enter password: ****
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.0.23 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
然后复制这段代码粘贴到mysql>这里,创建数据库和表的内容:
create database springboot charset utf8;
use springboot;
create table t_user (
id int primary key auto_increment,
username varchar(200) not null unique,
password varchar(200)
)engine=InnoDB charset utf8;
insert into t_user(username,password) values ('admin','123');
insert into t_user(username,password) values ('tom','456');
只说这么多。总之就是建一个叫做springboot的数据库,里面再加一个表t_user,表里加一些数据内容。
接下来如下:
@Test注解导入包时 不要误导入import org.junit.jupiter.api.Test
正确 import org.junit.Test;
注意:
springboot版本2.2.x之前是使用的org.junit.Test测试类
springboot版本2.2.x之后使用的org.junit.jupiter.api.Test测试类
package com.itany;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.sql.DataSource;
import java.sql.SQLException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot06JdbcApplicationTests {
@Autowired
private DataSource dataSource;
@Test
public void contextLoads() throws SQLException {
System.out.println("--------------");
System.out.println("DataSource类型:"+dataSource.getClass());
System.out.println("Connection连接:"+dataSource.getConnection());
}
}
如果没有出现报错,结果应该是:
如果出现错误,就加上或修改:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
只修改上一行,会出现结果:
DataSource类型:class org.apache.commons.dbcp.BasicDataSource
如果再加上下面,这些:
<!-- 还要加上以下:-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--Druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.13-SNSAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
会出现结果:这个结果对于配置连接池参数,也会出现另外一种结果。记住
DataSource类型:class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper。在配置连接池参数时会再次见到。
4. 配置连接池参数
使用DataSource类型:class org.apache.commons.dbcp.BasicDataSource的配置之后,下面的连接池参数并不会立即生效:
spring.datasource.initialSize=10
spring.datasource.maxActive=100
spring.datasource.minIdle=5
spring.datasource.maxWait=50000
问题:添加上面的参数后并不生效,因为SpringBoot默认并不支持这些参数(DataSourceProperties)
打断点之后调试DeBug运行:可以看到设置没有生效。
解决:自定义数据源配置,因为BasicDataSource.class里面有initialSize、maxActive、minIdle
等。
所以return new BasicDataSource()。
package com.itany.config;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
// 从配置属性文件中读取spring.datasource属性,并注入给数据源的属性
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return new BasicDataSource();
}
}
然后就能看到:配置生效了。注意这个文件放在什么位置。
不建议如下:因为我暂时也不懂,嘿嘿!!
但是我们如果使用了DataSource类型:class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper,然后加上如下配置之后,就会立即生效:
spring.datasource.druid.initial-size=10
spring.datasource.druid.max-active=100
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-wait=50000
打断点之后调试DeBug运行:可以看到设置立即生效。
这是因为:
5. 使用JdbcTemplate操作数据库
注入JdbcTemplate ==>jdbc模板,就可以调用里面的方法queryForList()==》查询列表()。
package com.itany.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
//注入JdbcTemplate ==>jdbc模板
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping("/findAll")
@ResponseBody //响应到浏览器
public List<Map<String, Object>> findAll(){
//调用jdbcTemplate里面的方法queryForList()==》查询列表()。
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from t_user");
return list;
}
}
结果:
2. MyBatis
2.1 基本用法
步骤:
1. 创建一个工程,选择以下模块:Web、MySQL、MyBatis
添加:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.5</version>
</dependency>
新建数据库:
新建pojo一个User.class
package com.itany.pojo;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
新建dao层:
package com.itany.mapper;
import com.itany.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectAll();
public User selectById(Integer id);
public void insert(User user);
}
在resource中建一个mapper目录,写映射文件UserMapper.xml:
先去MyBatis官网mybatis – MyBatis 3 | 入门中寻找这个映射中文件的格式 示例:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
然后修改成自己需要的:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itany.mapper.UserMapper">
<select id="selectAll" resultType="User">
select * from t_user ;
</select>
</mapper>
2. 配置application.yml
#配置DataSource
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?userUnicode=true&characterEncoding=utf-8
username: root
password: root
initialSize: 5
maxActive: 100
minIdle: 3
maxWait: 50000
#配置MyBatis 别名包com.itany.pojo 和 映射文件的路径classpath:mapper/*.xml
mybatis:
type-aliases-package: com.itany.pojo
mapper-locations: classpath:mapper/*.xml
3. 编写Mapper、Service、Controller
继续编写映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itany.mapper.UserMapper">
<sql id="userColumn">
id,
username,
password
</sql>
<select id="selectAll" resultType="User">
select <include refid="userColumn"/> from t_user ;
</select>
<select id="selectById" resultType="User">
select <include refid="userColumn"/> from t_user where id=#{id};
</select>
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into t_user (username,password) values (#{username},#{password});
</insert>
</mapper>
编写Service
package com.itany.service;
import com.itany.pojo.User;
import java.util.List;
public interface UserService {
public List<User> findAll();
public User findById(Integer id);
public void add(User user);
}
对应的是实现类implUserServiceImpl:
package com.itany.service.impl;
import com.itany.mapper.UserMapper;
import com.itany.pojo.User;
import com.itany.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
//事务和事务的传播属性 @ transactional(传播=传播。必需的,回滚 = Exception.class)
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public class implUserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
//@ transactional(传播=传播。支持只读= true)
@Override
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public List<User> findAll() {
return userMapper.selectAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public User findById(Integer id) {
return userMapper.selectById(id);
}
@Override
public void add(User user) {
userMapper.insert(user);
}
}
编写Controller:
package com.itany.controller;
import com.itany.pojo.User;
import com.itany.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
public List<User> findAll(){
return userService.findAll();
}
@RequestMapping("/findById")
public User findById(Integer id){
return userService.findById(id);
}
@RequestMapping("/add")
public User add(User user){
userService.add(user);
return user;
}
}
4. 配置MyBatisConfig
package com.itany.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration //扫描MyBatis的Mapper接口所在的包
@MapperScan("com.itany.mapper")
public class MyBatisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){//数据源的配置
return new DruidDataSource();
}
}
添加下面依赖,new DruidDataSource();才能找到。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.13-SNSAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
运行得到:如果端口已被占用,那yml中修改端口号:
server:
port: 8087
2.2 配置PageHelper分页插件
步骤:
1. 添加PageHelper的依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
还需要:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
2. 配置PageHelper的属性(yml中)
#配置PageHelper
page:
helper:
dialect: mysql
严谨一点多配一些:
pagehelper:
helper-dialect: mysql
reasonable: true
params: count=countSql
support-methods-arguments: true
page-size-zero: true
3. 使用PageHelper实现分页
注意:UserMapper.xml文件的id="selectAll"的查询方法末尾不能加分号。不然影响分页limit的添加。
<sql id="userColumn">
id,
username,
password
</sql>
<select id="selectAll" resultType="User">
select <include refid="userColumn"/> from t_user
</select>
UserService的接口中:
package com.itany.service;
import com.itany.pojo.User;
import java.util.List;
public interface UserService {
public List<User> findAll();
public User findById(Integer id);
public void add(User user);
//分页查找 传递参数 当前页码pageNum 、页的大小pageSize
public List<User> findByPage(int pageNum,int pageSize);
}
implUserServiceImpl实现类中:
package com.itany.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itany.mapper.UserMapper;
import com.itany.pojo.User;
import com.itany.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
//事务和事务的传播属性 @ transactional(传播=传播。必需的,回滚 = Exception.class)
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public class implUserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
//@ transactional(传播=传播。支持只读= true)
@Override
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public List<User> findAll() {
return userMapper.selectAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public User findById(Integer id) {
return userMapper.selectById(id);
}
@Override
public void add(User user) {
userMapper.insert(user);
}
//分页查找 传递参数 当前页码pageNum 、页的大小pageSize
@Override
public PageInfo<User> findByPage(int pageNum, int pageSize) {
//使用PageHelper去设置分页,开始页的:页码和页的大小。
PageHelper.startPage(pageNum,pageSize);//必须写在查询之前
/*
因为我们在yml中做了设置
且使用PageHelper.startPage(pageNum,pageSize);,
所以这里selectAll()会进行自动分页查找。
*/
List<User> users = userMapper.selectAll();
/*
返回数据的时候,我们不仅需要分页包含的数据users的信息,
还需要获得当前分页相关的的页码信息。所以要使用PageInfo。
new PageInfo<>(users)
返回值也改成return pageInfo;
所以interface UserService接口里也要改public PageInfo<User>。
*/
PageInfo<User> pageInfo = new PageInfo<>(users);
System.out.println(pageInfo.getTotal());
System.out.println(pageInfo.getPages());
System.out.println(pageInfo.getList());
System.out.println(pageInfo);
return pageInfo;
}
}
UserService的接口中:public List<User> 也要改:public PageInfo<User>
package com.itany.service;
import com.github.pagehelper.PageInfo;
import com.itany.pojo.User;
import java.util.List;
public interface UserService {
public List<User> findAll();
public User findById(Integer id);
public void add(User user);
//分页查找 传递参数 当前页码pageNum 、页的大小pageSize
public PageInfo<User> findByPage(int pageNum, int pageSize);
}
然后在Controller中:
@RequestMapping("/findByPage")
public PageInfo<User> findByPage(int pageNum) {
System.out.println("findByPage-------------------");
return userService.findByPage(pageNum,3);
}
UserMapper.java映射文件不用改 ,只要使用public List<User> selectAll();这个查询就够了。
假如报错:1.缺少依赖。2.UserMapper.xml文件中要使用这个查询语句获得分页的时候,后面会加缀limit,所以不能加分号。
1.缺少依赖。
2022-10-10 22:52:02.461 INFO 63860 --- [ main] c.itany.Springboot07MybatisApplication : Starting Springboot07MybatisApplication using Java 17.0.4.1 on DESKTOP-CU8LHVC with PID 63860 (E:\programmingSoftware\myCode\springboot07-mybatis\target\classes started by sly in E:\programmingSoftware\myCode\springboot07-mybatis)
2022-10-10 22:52:02.466 INFO 63860 --- [ main] c.itany.Springboot07MybatisApplication : No active profile set, falling back to 1 default profile: "default"
2022-10-10 22:52:02.694 WARN 63860 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.itany.Springboot07MybatisApplication]; nested exception is java.io.FileNotFoundException: class path resource [com/itany/service/UserService.class] cannot be opened because it does not exist
2022-10-10 22:52:02.734 ERROR 63860 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.itany.Springboot07MybatisApplication]; nested exception is java.io.FileNotFoundException: class path resource [com/itany/service/UserService.class] cannot be opened because it does not exist
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:188) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.4.jar:2.7.4]
at com.itany.Springboot07MybatisApplication.main(Springboot07MybatisApplication.java:10) ~[classes/:na]
Caused by: java.io.FileNotFoundException: class path resource [com/itany/service/UserService.class] cannot be opened because it does not exist
这就是因为缺少一些依赖造成的:
<!--page helper依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
<!-- <dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>-->
<!-- 集成mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
2.出现报错:不能加分号。
2022-10-10 22:59:53.393 ERROR 57876 --- [nio-8087-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 3' at line 6
### The error may exist in file [E:\programmingSoftware\myCode\springboot07-mybatis\target\classes\mapper\UserMapper.xml]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select id, username, password from t_user; LIMIT ?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 3' at line 6
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 3' at line 6] with root cause
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 3' at line 6
是因为在:UserMapper.xml文件中要使用这个查询语句获得分页的时候,后面会加缀limit,所以不能加分号。
最后附上完整地pom文件和yml文件:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itany</groupId>
<artifactId>springboot07-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot07-mybatis</name>
<description>springboot07-mybatis</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.4</version>
</dependency>
<!-- 集成mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.13-SNSAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--page helper依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
<!--commons-lang3包含很多基础的工具类,具体请百度-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!--有很多关于commons-lang3的介绍
commons-lang3(常用) - 简书
https://www.jianshu.com/p/1886903ed14c
(36条消息) Apache-commons-lang3方法应用_LzwGlory的博客-CSDN博客
https://blog.csdn.net/lzwglory/article/details/19822843
commons-lang3和commons-lang的区别:
lang3是Apache Commons 团队发布的工具包,要求jdk版本在1.5以上,相对于lang来说完全支持java5的特性,废除了一些旧的API。该版本无法兼容旧有版本,于是为了避免冲突改名为lang3。
lang包可以说是废弃了,以后请不要使用。采用lang3直接代替即可
-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml :
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8
username: root
password: root
initialSize: 5
maxActive: 100
minIdle: 3
maxWait: 50000
server:
port: 8087
mybatis:
type-aliases-package: com.itany.pojo
mapper-locations: classpath:mapper/*.xml
pagehelper:
helper-dialect: mysql
reasonable: true
params: count=countSql
support-methods-arguments: true
page-size-zero: true
还有UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itany.mapper.UserMapper">
<sql id="userColumn">
id,
username,
password
</sql>
<select id="selectAll" resultType="User">
select <include refid="userColumn"/> from t_user
</select>
<select id="selectById" resultType="User">
select <include refid="userColumn"/> from t_user where id=#{id};
</select>
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into t_user (username,password) values (#{username},#{password});
</insert>
</mapper>
还有MyBatisConfig
package com.itany.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration //扫描MyBatis的Mapper接口所在的包
@MapperScan("com.itany.mapper")
public class MyBatisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){//数据源的配置
return new DruidDataSource();
}
}
2.3 使用MyBatis-Plus (MyBatis的增强工具,对MyBatis的只增强不改变,简化开发,提高效率。)
参考:MyBatis-Plus https://baomidou.com/
简介 | MyBatis-Plus https://baomidou.com/pages/24112f/
步骤:
创建新的工程:
1. 引入mybatis-plus的依赖(starter) 和 连接池druid 依赖。
<!--mybatis-plus的依赖(starter)-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.13-SNSAPSHOT</version>
</dependency>
2. 配置application.yml
老版本如下配置:现在一些数值都改变了
# DataSource Config
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.itany.pojo
global-config:
db-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)",3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
#mp2.3+ 全局表前缀 t_
#table-prefix: t_
#刷新mapper 调试神器
refresh-mapper: true
#逻辑删除配置(下面3个配置)
logic-delete-value: 1
logic-not-delete-value: 0
#sql 注射器
sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
map-under-score-to-camel-case: true
cache-enabled: false
详细介绍使用配置 | MyBatis-Plus https://baomidou.com/pages/56bac0/#基本配置
# DataSource Config
mybatis-plus:
#如果是放在src/main/java目录下:classpath:/com/yourpackage/*/mapper/*Mapper.xml
#如果是放在resource目录classpath:/mapper/*Mapper.xml
mapper-locations: classpath:mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号隔开
type-aliases-package: com.itany.pojo
global-config:
db-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)",3:"全局唯一ID UUID";
id-type: auto
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
#mp2.3+ 全局表前缀 t_
#table-prefix: t_
#刷新mapper 调试神器 (修改mapper,就不用重启了。)
refresh-mapper: true
#逻辑删除配置(下面3个配置)
logic-delete-value: 1
logic-not-delete-value: 0
#sql 注射器
sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
map-under-score-to-camel-case: true
cache-enabled: false
现在我是这样配置的(2022-10-17):比较简略的配置,只要两行。
#配置DataSource
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?userUnicode=true&characterEncoding=utf-8
username: root
password: root
initialSize: 5
maxActive: 100
minIdle: 3
maxWait: 50000
main:
allow-circular-references: true
server:
port: 8087
# DataSource Config
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.itany.pojo
3. 配置MybatisPlusConfig
package com.itany.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
@MapperScan("com.itany.mapper") //扫描mapper的接口所在的包
public class MybatisPlusConfig {
/*
* 分页插件,自动识别数据库类型
* */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/*
* 数据源
* */
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return new DruidDataSource();
}
}
数据源的配置从xml配置文件读取:
编写User.java:
package com.itany.pojo;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
4. 编写Mapper,继承BaseMapper
package com.itany.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itany.pojo.User;
/*mapper
* 只需要继承BaserMapper<User>就可以了。
* BaseMapper把基本的增删改查都写好了。
* */
public interface UserMapper extends BaseMapper<User> {
}
这个映射文件UserMapper.xml:都不用写了。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itany.mapper.UserMapper">
<sql id="userColumn">
id,
username,
password
</sql>
<select id="selectAll" resultType="User">
select <include refid="userColumn"/> from t_user
</select>
<select id="selectById" resultType="User">
select <include refid="userColumn"/> from t_user where id=#{id};
</select>
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into t_user (username,password) values (#{username},#{password});
</insert>
</mapper>
直接测试:
报错:找不到springboot数据库中的user表。是因为表名对不上,数据库中是t_user表,现在查的是user表。
所以可以在配置文件中,给表名加一个前缀t_。
然后再次运行:找到了。
还可以给实体类加注解:@TableName("t_user") //指定对应的数据库表名。
继续测试:
2.3.1条件构造器
还有一个很好用的条件构造器,实现条件查询:
以前是这样的:现在已经无效了。
现在如下:
@Test
public void findByPage(){
System.out.println("---------");
//定义条件构造器,用来封装查询条件
QueryWrapper<User> queryWrapperSelectOne = new QueryWrapper<>();
//只查询一条记录,selectOne,且结果只有一条,最好是查id,不然查出多条会报错。
queryWrapperSelectOne.eq("username","alice");
User user = userMapper.selectOne(queryWrapperSelectOne);
System.out.println("selectOne-----user:"+user);
QueryWrapper<User> queryWrapperSelectCount = new QueryWrapper<>();
//查询总记录数目
queryWrapperSelectCount.eq("username","mom");
Integer count = Math.toIntExact(userMapper.selectCount(queryWrapperSelectCount));
System.out.println("selectCount-----count:"+count);
QueryWrapper<User> queryWrapperSelectList = new QueryWrapper<>();
//查询所有记录:返回实体列表List<user>
queryWrapperSelectList.eq("username","jack");
List<User> list= userMapper.selectList(queryWrapperSelectList);
System.out.println("selectList-----list:"+list);
System.out.println("--------==============-");
QueryWrapper<User> queryWrapperSelectMaps = new QueryWrapper<>();
//查询所有记录:返回map列表 List<Map<String,Object>>
queryWrapperSelectMaps.eq("username","mom");
List<Map<String, Object>> listMap= userMapper.selectMaps(queryWrapperSelectMaps);
System.out.println("selectMaps-----listMap:"+listMap);
System.out.println("--------==============-");
QueryWrapper<User> queryWrapperSelectPage = new QueryWrapper<>();
queryWrapperSelectPage.like("username","o");
Page<User> page = new Page<>(2,2);
userMapper.selectPage(page,null);
System.out.println("当前页数据:"+page.getRecords());
System.out.println("总分页数量:"+page.getPages());
System.out.println("总记录数量:"+page.getTotal());
System.out.println("是否有下一页:"+page.hasNext());
System.out.println("是否有上一页:"+page.hasPrevious());
Page<User> result = userMapper.selectPage(page,queryWrapperSelectPage);
System.out.println("selectPage--result:"+result);
System.out.println("selectPage--result.getCurrent():"+result.getCurrent());
System.out.println("selectPage--result.setPages(2):"+result.setPages(2));
System.out.println("selectPage--result.setSize(3):"+result.setSize(3));
System.out.println("selectPage--result.getPages():"+result.setCurrent(1));
System.out.println("selectPage--result.getPages():"+result.getPages());
System.out.println("selectPage--result.getSize():"+result.getSize());
System.out.println("selectPage--result.getCurrent():"+result.getCurrent());
System.out.println("selectPage--result.getRecords():"+result.getRecords());
System.out.println("selectPage--result.getTotal():"+result.getTotal());
//查询所有记录并翻页:返回Ipage<entity>
IPage<User> userIPage = userMapper.selectPage(page,queryWrapperSelectPage);
System.out.println("selectPage--userIPage:"+userIPage);
System.out.println("selectPage--userIPage.getRecords01:"+userIPage.getRecords());
System.out.println("selectPage--userIPage.getTotal:"+ userIPage.getTotal());
System.out.println("selectPage--userIPage.getPage:"+userIPage.getPages());
System.out.println("selectPage--userIPage.getCurrent():"+userIPage.getCurrent());
System.out.println("selectPage--userIPage.setPages(1):"+userIPage.setPages(1));
System.out.println("selectPage--userIPage.setCurrent(1):"+userIPage.setCurrent(1));
System.out.println("selectPage--userIPage.setSize(1):"+userIPage.setSize(1));
System.out.println("selectPage--userIPage.getRecords02:"+userIPage.getRecords());
System.out.println("selectPage--queryWrapperSelectPage(02):"+queryWrapperSelectPage.getSqlSelect());
System.out.println("selectPage--userIPage.orders():"+userIPage.orders());
System.out.println("--------==============-");
//查询所有记录并翻页:返回Ipage<Map<String, Object>>
Page<Map<String,Object>> MapsPage = new Page<>(1,2);
QueryWrapper<User> queryWrapperSelectMapsPage = new QueryWrapper<>();
IPage<Map<String,Object>> mapIPage = userMapper.selectMapsPage(MapsPage,queryWrapperSelectMapsPage);
System.out.println("selectMapsPage-----mapIPage:"+mapIPage);
System.out.println("selectMapsPage--mapIPage.optimizeCountSql():"+mapIPage.optimizeCountSql());
System.out.println("selectMapsPage--mapIPage.optimizeJoinOfCountSql():"+mapIPage.optimizeJoinOfCountSql());
System.out.println("--------==============-");
//查询所有记录:lambda
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.likeRight(User::getUsername,"m").and(lqw->lqw.le(User::getId,3).or().isNotNull(User::getPassword));
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
System.out.println("selectAll-----userList:");
userList.forEach(System.out::println);
System.out.println("--------==============-");
//查询所有记录:chain
List<User> userList1 =
new LambdaQueryChainWrapper<User>(userMapper).like(User::getUsername,"j").ge(User::getId,3).list();
System.out.println("LambdaQueryChainWrapper-----userList1:");
userList1.forEach(System.out::println);
//更新 修改
//形式1 Wrapper
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper.eq("id",3);
User user1 = new User();
user1.setUsername("jojo");
int result1 = userMapper.update(user1,userUpdateWrapper);
//形式2 whereWrapper
User whereUser = new User();
whereUser.setId(4);
UpdateWrapper<User> userUpdateWrapper1 = new UpdateWrapper<User>(whereUser);
User user2 = new User();
user2.setUsername("zhzn");
int result2 = userMapper.update(user2,userUpdateWrapper);
}
测试结果:
2.3.2 mybatis-plus 分页插件:
还有分页:以前是这样的,现在也无效了。
大家之前肯定都用过PageHelper来进行分页,其实mybatis-plus中也提供了一个分页插件PaginationInnerInterceptor,其实分页的本质就是内部封装了一个拦截器,对于满足条件的数据进行过滤处理。
配置分页插件:
package com.itany.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
@MapperScan("com.itany.mapper") //扫描mapper的接口所在的包
public class MybatisPlusConfig {
/*
* 分页插件,自动识别数据库类型
* */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件(必须要写,不然分页不起作用)
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/*
* 数据源
* */
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return new DruidDataSource();
}
}
注意:因为PaginationInnerInterceptor支持好几种数据库类型,DbType根据类型获取应使用的分页方案。
在mapper提供的API中就有进行分页的方法selectPage,一个是Page对象,一个是Wrapper条件构造器对象。(就是将用wrapper对象筛选出符合条件的数据,然后根据page对象进行分页)
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);
@Test
public void testPage(){
Page<User> page = new Page<>(2, 3);
userMapper.selectPage(page, null);
System.out.println("当前页数据:"+page.getRecords());
System.out.println("总分页数量:"+page.getPages());
System.out.println("总记录数量:"+page.getTotal());
System.out.println("是否有下一页:"+page.hasNext());
System.out.println("是否有上一页:"+page.hasPrevious());
}
结果:
2.3.3 自定义分页功能
有时候可能mybatis-plus中mapper提供的API不足以满足我们从查询要求,那么此时就需要我们自定义一个分页。
配置分页插件之后,
UserMapper.java:
package com.itany.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itany.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/*mapper
* 只需要继承BaserMapper<User>就可以了。
* BaseMapper把基本的增删改查都写好了。
* */
public interface UserMapper extends BaseMapper<User> {
/**
* 通过年龄查询用户信息并分页
* @param page MyBatis-Plus所提供的分页对象,必须位于第一个参数的位置
*/
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("username") String username);
}
注意:mybatis-plus提供的分页对象,必须位于第一个参数的位置。
src/main/resources/mapper/UserMapper.xml
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itany.mapper.UserMapper">
<!--Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);-->
<select id="selectPageVo" resultType="User">
select * from t_user where username like '%' #{username} '%'
</select>
</mapper>
测试类:
@Test
public void testPageVo(){
Page<User> page = new Page<>(1, 3);
userMapper.selectPageVo(page, "j");
System.out.println(page.getRecords());
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.hasNext());
System.out.println(page.hasPrevious());
}
可以发现,我们在xml文件中写的sql并没有实现分页功能,而是在mapper文件中传输过来时已经帮我们实现好了。
lombok的使用
补充:lombok的使用(可以简化代码,创建类的时候,不用写getXX()和setXX()方法了)。
步骤:
1. 添加lombok的依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<scope>代表不参与打包,等项目上线的时候,这个依赖已经在开发的时候帮我们生成重复性的getXX()和setXX()方法代码。
2. 使用lombok提供的注解
package com.itany.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.io.Serializable;
/*
* lombok提供了许多注解,标注在类上或属性上
* 常用注解:@Getter、@Setter、@ToString、@EqualsAndHashCode(等号)、@RequiredArgsConstructor.
* 以上五个注解全部加起来等价于@Data这个注解。
* */
/*
@Getter
@Setter
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
*/
@Data
@TableName("t_user") //指定对应的数据库表名
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}
还有其他常用注解,可以看lombok官方文档 Stable https://projectlombok.org/features/
Lombok - 简书 https://www.jianshu.com/p/ae1cd9ad6729
@Data 注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法
@Setter :注解在属性上;为属性提供 setting 方法
@Getter :注解在属性上;为属性提供 getting 方法
@Log4j :注解在类上;为类提供一个 属性名为log 的 log4j 日志对象
@NoArgsConstructor :注解在类上;为类提供一个无参的构造方法
@AllArgsConstructor :注解在类上;为类提供一个全参的构造方法
@Cleanup : 可以关闭流
@Builder : 被注解的类加个构造者模式
@Synchronized : 加个同步锁
@SneakyThrows : 等同于try/catch 捕获异常
@NonNull : 如果给参数加个这个注解 参数为null会抛出空指针异常
@Value : 注解和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法。
@toString:注解在类上;为类提供toString方法(可以添加排除和依赖);
官方文档https://projectlombok.org/features/index.html
3. 在IDEA安装lombok插件
由于源代码中并没有getter/setter等的定义,IDEA默认无法识别,会报错,需要安装lombok插件。
十二、SpringBoot整合Redis
Redis的学习和使用可以参考:
(52条消息) 2022最新版Redis入门到精通(云课堂视频学习笔记)_时时师师的博客-CSDN博客
1. 简介
Redis是一个内存数据库,可以作为缓存、消息中间件、keyvalue数据库等来使用。
具体参考:
(52条消息) 2022最新版Redis入门到精通(云课堂视频学习笔记)_时时师师的博客-CSDN博客
中的:
Radis的Java客户端-Jedis客户端。
Radis的Java客户端-SpringDataRedis客户端。
2. 操作
步骤:
新建项目,选择如下

1. 添加相关依赖
<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.4</version>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4.2</version>
</dependency>
假如我们不想使用lettuce客户端,那么可以修改:
<!--整合redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!--springboot2.0默认使用的redis客户端是lettuce-->
<!--使用exclusions把lettuce排除在外(exclusions n. 除外(exclusion 的复数形式);排除给付;除外条款)-->
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
注:在SpringBoot1.0中使用的Redis客户端是Jedis,在SpringBoot2.0中使用的Redis客户端是lettuce。
以下项目使用的Redis客户端是Jedis。
2. 配置redis
spring:
redis:
host: 192.168.1.107
port: 6379
password: 123456
lettuce: #这里可以选择lettuce也可以选择Jedis,但是spring默认使用lettuce。要使用Jedis需要额外引入依赖并配置。
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 100 #连接等待时间
3. 基本用法
使用SpringDataRedis提供的工具:
(操作字符串)StringRedisTemplate、(操作对象)RedisTemplate(需要RedisConfig!!自定义RedisTemplate的序列化方式)。
package com.redis.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.net.UnknownHostException;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//创建RedisTemplate对象
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
//创建连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer
= new GenericJackson2JsonRedisSerializer();
//key和HashKey采用String序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
//value和HashValue采用JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
推荐使用:Spring默认提供了一个StringRedisTemplate类,它的Key和Value的序列化方式默认就是String方式。不再需要RedisConfig!!如下:
package com.redis;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.redis.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
//JSON工具:ObjectMapper是SpringMVC里面默认的JSON处理工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testString() {
//插入一条string类型的数据
stringRedisTemplate.opsForValue().set("name","李四");
//读取一条string类型的数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = "+ name);
//插入一条string类型的数据
stringRedisTemplate.opsForValue().set("nameS","李小四");
//读取一条string类型的数据
Object nameS = stringRedisTemplate.opsForValue().get("nameS");
System.out.println("nameS = "+ nameS);
}
@Test
void testStringTemplate() throws JsonProcessingException {
//准备对象
User user = new User("夏至",18);
//手动序列化,转JSON,变成字符串
String json = mapper.writeValueAsString(user);
//插入一条数据到redis
stringRedisTemplate.opsForValue().set("user:200",json);
//读取数据,是一个JSON字符串
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
//反序列化,把字符串转成User对象。
User user1 = mapper.readValue(jsonUser,User.class);
System.out.println("user1 = "+ user1);
}
@Test
void testHash(){
//存
stringRedisTemplate.opsForHash().put("user:400","name","冬至");
stringRedisTemplate.opsForHash().put("user:400","age","21");
//取
Map<Object,Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
System.out.println("entries: "+ entries);
}
}