SpringBoot学习笔记

发布于:2024-05-19 ⋅ 阅读:(181) ⋅ 点赞:(0)

文章目录

1 SpringBoot 基本介绍

1.1 官方文档

1.1.1 官网: https://spring.io/projects/spring-boot
1.1.2 学习文档: https://docs.spring.io/spring-boot/docs/current/reference/html/
1.1.3 离线文档: spring-boot-reference.pdf
1.1.4 在线 API: https://docs.spring.io/spring-boot/docs/current/api/

1.2 Spring Boot 是什么?

1.2.1 第一句话: Spring Boot 可以轻松创建独立的、生产级的基于 Spring 的应用程序
1.2.2 第二句话: Spring Boot 直接嵌入 Tomcat、Jetty 或 Undertow ,可以"直接运行" SpringBoot 应用程序

1.3 SpringBoot 快速入门

1.3.1 需求/图解说明

● 构建一个 SpringBoot 项目,浏览器发送/hello 请求 [http://localhost:8080/hello],响应Hello,SpringBoot

1.3.2 完成步骤

  1. 确认开发环境是 jdk 8 或以上,maven 在 3.5+

  2. 在 springboot2\01_quickstart\pom.xml 引入 SpringBoot 父工程和 web 项目场景启动器

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>SpringBootQuickStart</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>SpringBootQuickStart</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <mysql.version>5.1.49</mysql.version>
  </properties>

  <!--导入SpringBoot父工程,规定的写法-->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.3</version>
  </parent>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <!--  导入web项目场景启动器,会自动导入和web开发相关的所有依赖,非常方便-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>
</project>

  1. 创 建 springboot2\01_quickstart\src\main\java\com\hspedu\springboot\ MainApp. 是 javaSpringBoot 应用主程序
package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/7 14:45
 */
@SpringBootApplication(scanBasePackages = {"org.example", "a"})
public class MainApp {
    // 启动SpringBoot应用程序
    public static void main(String[] args) {
        // 启动springboot应用程序/项目
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);

        // 如何查看容器中注入的组件
        String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

  1. 运行 MainApp.java 完成测试, 浏览器 http://localhost:8080/hello
    在这里插入图片描述

1.3.3 快速入门小结

  1. SpringBoot 比较传统的 SSM 开发, 简化整合步骤, 提高开发效率
  2. 简化了 Maven 项目的 pom.xml 依赖导入, 可以说是一键导入, 如图.
    在这里插入图片描述
  3. 引入一个 spring-boot-starter-web, 到底发生了什么? 一图胜千言
    在这里插入图片描述
  4. 内置 Tomcat , 简化服务器的配置
  5. 当然 SpringBoot 还有很多优势,后面老韩逐步深入讲解

1.4 Spring SpringMVC SpringBoot 的关系

  1. 他们的关系大概是: Spring Boot > Spring > Spring MVC
  2. Spring MVC 只是 Spring 处理 WEB 层请求的一个模块/组件, Spring MVC 的基石是Servlet
  3. Spring 的核心是 IOC 和 AOP, IOC 提供了依赖注入的容器 , AOP 解决了面向切面编程
  4. Spring Boot 是为了简化开发, 推出的封神框架(约定优于配置[COC],简化了 Spring 项目的配置流程), SpringBoot 包含很多组件/框架,Spring就是最核心的内容之一,也包含 SpringMVC
  5. Spring 家族,有众多衍生框架和组件例如 boot、security、jpa 等, 他们的基础都是 Spring

1.4.2 如何理解 -约定优于配置

1、约定优于配置(Convention over Configuration/COC),又称按约定编程,是一种软件设计规范, 本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)

2、例如在模型中存在一个名为 User 的类,那么对应到数据库会存在一个名为 user 的表,只有在偏离这个约定时才需要做相关的配置 (例如你想将表名命名为 t_user 等非 user 时才需要写关于这个名字的配置)

3、简单来说就是假如你所期待的配置与约定的配置一致,那么就可以不做任何配置,约定不符合期待时, 才需要对约定进行替换配置

4、约定优于配置理念【老韩解读:为什么要搞一个约定优于配置】约定其实就是一种规范,遵循了规范,那么就存在通用性,存在通用性,那么事情就会变得相对简单,程序员之间的沟通成本会降低,工作效率会提升,合作也会变得更加简单

2 依赖管理和自动配置

2.1 依赖管理

2.1.1 什么是依赖管理

  1. spring-boot-starter-parent 还有父项目, 声明了开发中常用的依赖的版本号
  2. 并且进行 自动版本仲裁 , 即如果程序员没有指定某个依赖 jar 的版本,则以父项目指定的版本为准
    在这里插入图片描述

2.1.2 修改自动仲裁/默认版本号

  1. 需求说明: 将 SpringBoot mysql 驱动修改成 5.1.49
    在这里插入图片描述

修改版本仲裁:

  1. 方式1:显示导入的mysql依赖。并明确的指定version
  2. 方式2:在自己的pom.xml文件中,另用指定mysql的key

都是在pom.xml文件中修改。

方式1
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.49</version>
    </dependency>
方式2
  <!-- 方式2
  在properties中添加这个,指定key,然后后面的dependency中就不用写这个对应的key了
    <properties>
      <mysql.version>5.1.49</mysql.version>
  </properties>
  -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
  
      </dependency>

2.2 starter 场景启动器

2.2.1 starter 场景启动器基本介绍

  1. 开发中我们引入了相关场景的 starter,这个场景中所有的相关依赖都引入进来了,比如我们做 web 开发引入了,该 starter 将导入与 web 开发相关的所有包
    在这里插入图片描述
    导入的包如下:
    在这里插入图片描述
  2. 依赖树 : 可以看到 spring-boot-starter-web ,帮我们引入了 spring-webmvc,spring-web开发模块,还引入了 spring-boot-starter-tomcat 场景,spring-boot-starter-json 场景,这些场景下面又引入了一大堆相关的包,这些依赖项可以快速启动和运行一个项目,提高开发效率
    在这里插入图片描述
    在这里插入图片描述
  3. 所有场景启动器最基本的依赖就是 spring-boot-starter , 前面的依赖树分析可以看到,这个依赖也就是 SpringBoot 自动配置的核心依赖
    在这里插入图片描述

2.2.2 官方提供的 starter

https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters

  1. 在开发中我们经常会用到 spring-boot-starter-xxx ,比如 spring-boot-starter-web,该场景是用作 web 开发,也就是说 xxx 是某种开发场景。
  2. 我们只要引入 starter,这个场景的所有常规需要的依赖我们都自动引入。
  3. SpringBoot2 支 持 的 所 有 场 景 如 下 :
    https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
    在这里插入图片描述

2.2.3 第三方 starter

  1. SpringBoot 也支持第三方 starter
  2. 第三方 starter 不要从 spring-boot 开始,因为这是官方 spring-boot 保留的命名方式的。第三方启动程序通常以项目名称开头。例如,名为 thirdpartyproject 的第三方启动程序项目通常被命名为 thirdpartyproject-spring-boot-starter。
  3. 也就是说:xxx-spring-boot-starter 是第三方为我们提供的简化开发的场景启动器

2.3 自动配置

2.3.1 自动配置基本介绍

  1. 小伙伴还记得否,前面学习 SSM 整合时,需要配置 Tomcat 、配置 SpringMVC、配置如何扫描包、配置字符过滤器、配置视图解析器、文件上传等[如图],非常麻烦。而在SpringBoot 中,存在自动配置机制,提高开发效率

  2. 简单回顾以前 SSM 整合的配置.
    在这里插入图片描述
    在这里插入图片描述

2.3.2 SpringBoot 自动配置了哪些?

  1. 自动配置 Tomcat
    在这里插入图片描述2. 自动配置 SpringMVC
    在这里插入图片描述
  2. 自动配置 Web 常用功能: 比如字符过滤器, 老韩提示: 通过获取 ioc 容器,查看容器创建 的 组 件 来 验 证 , 修 改
    D:\hsp_springboot_temp\01-quickstart\src\main\java\com\hspedu\springboot\MainApp.java
package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/7 14:45
 */
@SpringBootApplication(scanBasePackages = {"org.example", "a"})
public class MainApp {
    // 启动SpringBoot应用程序
    public static void main(String[] args) {
        // 启动springboot应用程序/项目
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);

        // 如何查看容器中注入的组件
        String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

在这里插入图片描述
4. 自 动 配 置 : 默 认 扫 描 包 结 构 !!! , 官 方 文 档
和MyApp.java 在同一个包下(一般是package org.example)的所有配置都会被扫描。但是如果不在同一个包,就会扫描不到。会报错:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Jun 24 14:56:23 CST 2019

There was an unexpected error (type=Not Found, status=404).

No message available

在这里插入图片描述

2.3.3 如何修改默认配置

  1. 需求:要求能扫描 com.hspedu 包下的 HiController.java 应该如何处理?
  2. 创建: springboot2\01_quickstart\src\main\java\com\hspedu\HiController.java, 并测试,这时是访问不到的.
  3. 修改 MainApp.java, 增加扫描的包, 并完成测试.
    @SpringBootApplication(scanBasePackages = {"org.example", "a"})
package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/7 14:45
 */
@SpringBootApplication(scanBasePackages = {"org.example", "a"})
public class MainApp {
    // 启动SpringBoot应用程序
    public static void main(String[] args) {
        // 启动springboot应用程序/项目
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);

        // 如何查看容器中注入的组件
        String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

2.3.3.2 resources\application.properties 配置大全
  • SpringBoot 项目最重要也是最核心的配置文件就是 application.properties,所有的框架配
    置都可以在这个配置文件中说明
  • 地址:
2.3.3.3 resources\application.properties 修改配置

● 各 种 配 置 都 有 默 认 , 可 以 在 resources\application.properties 修 改 , application.properties 文件我们可以手动创建
在这里插入图片描述

# 修改server监听端口8080
server.port=10010

# 修改文件上传的大小
# 这些配置是在哪里读取的?
# 默认配置最终都是映射到某个类。例如multipart.max-file-size 会映射/关联到MyltipartProperties 上。
# 将光标放在该属性上, 输入Ctrl + b 就可以定位这个属性是管理到哪个类的
spring.servlet.multipart.max-file-size=10MB
2.3.3.4 resources\application.properties 常用配置★
#端口号
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 数据库的索引编号(015)
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
2.3.3.5 resources\application.properties 自定义配置

● 还可以在 properties 文件中自定义配置,通过@Value(“${}”)获取对应属性值

application.properties 文件
my.website=https://www.baidu.com


某个 Bean@Value("${my.website}")
private String bdUrl;	// 为bdUrl添加注解。

2.3.4 SpringBoot 在哪配置读取 application.properites (@Deprecated了,不细看)

1、打开 ConfigFileApplicationListener.java , 看一下源码(按住Ctrl + N 然后输入ConfigFileApplicationListener就能打开这个文件。)

2.3.5 自动配置 遵守按需加载原则

  1. 自动配置遵守按需加载原则:也就是说,引入了哪个场景 starter 就会加载该场景关联的 jar 包,没有引入的 starter 则不会加载其关联 jar。
    在这里插入图片描述
  2. SpringBoot 所 有 的 自 动 配 置 功 能 都 在 spring-boot-autoconfigure 包 里 面
    在这里插入图片描述
  3. 在 SpringBoot 的 自 动 配 置 包 , 一 般 是 XxxAutoConfiguration.java, 对 应XxxxProperties.java, 如图
    在这里插入图片描述
    过程:
    首先XXProperties.java读取配置文件中要修改的配置,如果没有则全部都是默认值。
    然后XXProperties.java会作为JavaBean注入XXAutoConfiguration.java中,然后XXAutoConfiguration.java就可以用XXProperties.java中的内容。

3 容器功能

3.1 Spring 注入组件的注解

@Component、@Controller、 @Service、@Repository
说明: 这些在 Spring 中的传统注解仍然有效,通过这些注解可以给容器注入组件
在这里插入图片描述
在这里插入图片描述
编写JavaBean
在这里插入图片描述

package bean;

import org.springframework.stereotype.Repository;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/29 15:19
 */
@Repository
public class A {
}

  1. 在 springboot2\01_quickstart\src\main\java\com\hspedu\springboot\MainApp.java 获取, 完成测试
package org.example;

import bean.A;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/7 14:45
 */
@SpringBootApplication(scanBasePackages = {"org.example", "a", "bean"})
public class MainApp {
    // 启动SpringBoot应用程序
    public static void main(String[] args) {
        // 启动springboot应用程序/项目
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);
        System.out.println("启动springboot成功");

        A abean = ioc.getBean(A.class); // 要在自动扫描的包scanBasePackages里设置bean包才能扫描到里面的A
        System.out.println("aBean="+abean);
    }
}

3.2 @Configuration

:演示在 SpringBoot, 如何通过@Configuration 创建配置类来注入组件
● 回顾传统方式如何通过配置文件注入组件

    1. 创建 springboot2\01_quickstart\src\main\resources\beans.xml
      在这里插入图片描述

● 使用 SpringBoot 的@Configuration 添加/注入组件

  1. 创 建springboot2\01_quickstart\src\main\java\com\hspedu\springboot\config\BeanConfig.java
package springboot.example.config;

import org.springframework.context.annotation.Scope;
import springboot.example.bean.Monster;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/*
* 1. @Configuration 标识是一个 配置类,等价于配置文件。
* 2. 程序员可以通过 @Bean 注解注入bean对象到容器
* 3. 当一个类被@Configuration 标识,该类的Bean也会注入容器。
*  */
@Configuration
public class BeanConfig {

    /*
    * 1. @Bean: 表示给容器添加组件,就是Monster bean
    * 2. monster01(): 表示你的方法名 默认 作为 Bean的名字/id
    * 3. Monster: 注入类型,注入bean的类型是Monster
    * 4. new Monster(200, "牛魔王", 500, "疯魔拳"); 为注入的具体信息
    * 5. @Bean(name="Monster_nmw") 表示将默认的 Bean的名字/id 更改为 Monster_nmw
    * 6. 添加 @Scope("prototype") 表示返回的是多例
    * */
    @Bean
    @Scope("prototype")
    public Monster monster01() {
        return new Monster(200, "牛魔王", 500, "疯魔拳");
    }
}

  1. 修改 MainApp.java , 从配置文件/容器获取 bean , 并完成测试
    在这里插入图片描述

在这里插入图片描述

3.2.2 @Configuration 注意事项和细节

  1. 配置类本身也是组件, 因此也可以获取
//配置类本身也是组件, 因此也可以获取
BeanConfig beanConfig = ioc.getBean(BeanConfig.class);
System.out.println("beanConfig= " + beanConfig);
  1. SpringBoot2 新增特性: proxyBeanMethods 指定 Full 模式 和 Lite 模式
    在这里插入图片描述3. 配置类可以有多个, 就和 Spring 可以有多个 ioc 配置文件是一个道理.
    即BeanConfig可以有多个。在MainApp.java中都可以获取。

3.3 @Import

演示在 SpringBoot, 如何通过 @Import 来注入组件

// 注意@Import 方式注入的组件, 默认组件的名字就是全类名
@Import({Dog.class, Cat.class})
// @Configuration//标识这是一个配置类: 等价 配置文件
@Configuration(proxyBeanMethods = false)
public class BeanConfig {
}

3.4 @Conditional

  1. 条件装配:满足 Conditional 指定的条件,则进行组件注入
  2. @Conditional 是一个根注解,下面有很多扩展注解
    按住Ctrl + n, 输入conditional查看所有的条件装配注解:
    在这里插入图片描述
    在这里插入图片描述

3.4.2 应用实例

  1. 要求: 演示在 SpringBoot, 如何通过 @ConditionalOnBean 来注入组件
  2. 只有在容器中有 name = monster_nmw 组件时,才注入 dog01, 代码如图
@Import({Dog.class, Cat.class})
// @Configuration//标识这是一个配置类: 等价 配置文件
@Configuration(proxyBeanMethods = false)
public class BeanConfig {
	/*
	@ConditionalOnBean(name = "monster_nmw"):
	1. 表示只有容器中注入了 name = monster_nmw 的组件,下面的组件(dog01)才会被注入
	2. @ConditionalOnBean(name = "monster_nmw") 也可以放在类名处,则表示对该配置类中所有要注入的组件都进行条件约束
	3. 还有很多其它条件约束注解
	*/
	@ConditionalOnBean(name = "monster_nmw")
	@Bean
	public Dog dog01() {
		return new Dog();
	}
}

3.5 @ImportResource

3.5.1 作用:原生配置文件引入, 也就是可以直接导入 Spring 传统的 beans.xml ,可以认为是 SpringBoot 对 Spring 容器文件的兼容.
3.5.2 @ImportResource 应用实例

  1. 需求: 将 beans.xml 导入到 BeanConfig.java 配置类, 并测试是否可以获得 beans.xml注入/配置的组件
    在这里插入图片描述
  2. 修改 BeanConfig.java / 或者创建新的 BeanConfig3.java(建议创建新的配置类) 来测试,使用@ImportResource 导入 beans.xml
package springboot.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/30 23:55
 */
@Configuration
@ImportResource(locations = "classpath:beans.xml")	// 这里的classpath:就是 SpringBootProjects/SpringBootQuickStart/src/main/resources
// 导入多个时,写法:@ImportResource(locations = {"classpath:beans.xml", "classpath:beans2.xml"})
public class BeanConfig2 {

}

  1. 在MainApp.java中测试
package springboot.example;

import springboot.example.bean.Monster;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.awt.print.Pageable;

/**
 * @Author: GQLiu
 * @DATE: 2024/4/7 14:45
 * Page 113
 */
@SpringBootApplication()
public class MainApp {
    // 启动SpringBoot应用程序
    public static void main(String[] args) {
        // 启动springboot应用程序/项目
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);

        // 测试@ImportResource
        System.out.println("monster03:" + ioc.getBean("monster03"));
        System.out.println(ioc.getBean("monster03"));
    }
}

3.6 配置绑定

一句话:使用 Java 读取到 SpringBoot 核心配置文件 application.properties 的内容,并且把它封装到 JavaBean 中

  1. 需求: 将 application.properties 指定的 k-v 和 JavaBean 绑定
// 在application.properties中配置增加下面的属性
#设置属性 k-v
furn01.id=100
furn01.name=soft_chair!!
furn01.price=45678.9
  1. 编写JavaBean:E:\JavaCode\SpringBootProjects\SpringBootQuickStart\src\main\java\springboot\example\bean\Furn.java
package springboot.example.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/1 13:12
 */
/*
* 1. @Component 将Furn注册为一个组件
* 2. @ConfigurationProperties(prefix = "furn01") 指定在application.properties前置,这样Furn组件就会与 属性文件中的值绑定了。
* */
@Component
@ConfigurationProperties(prefix = "furn01")
public class Furn {
    private Integer id;
    private String name;
    private Double price;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }
}

  1. 修改 HelloController.java,增加如下代码:

    @Autowired
    Furn furn;
    @RequestMapping("/furn")
    @ResponseBody
    public Furn furn() {
        return furn;
    }
  1. 启动 SpringBoot 主程序,测试

在这里插入图片描述

  1. 配置绑定还有第 2 种方式, 也给小伙伴演示下, 完成测试,效果一样, 注意: 注销@Component 需 要 在 BeanConfig.java( 老 韩 说 明 : 也 可 以 是 其 它 配 置 类 ) 配 置@EnableConfigurationProperties(Furn.class), 否则会提示错误

在这里插入图片描述
在这里插入图片描述

3.6.3 注意事项和细节

  1. 如果 application.properties 有中文, 需要转成 unicode 编码写入, 否则出现乱码
soft_chair\u6c99\u53d1!!
  1. 使用 @ConfigurationProperties(prefix = “furn01”) 会提示如下信息, 但是不会影响使用
    在这里插入图片描述

5 Lombok

Lombok 作用

  1. 简化 JavaBean 开发, 可以使用 Lombok 的注解让代码更加简洁
  2. Java 项目中,很多没有技术含量又必须存在的代码:POJO 的 getter/setter/toString;异常处理;I/O 流的关闭操作等等,这些代码既没有技术含量,又影响着代码的美观,Lombok应运而生
  • 常用方法
    在这里插入图片描述
    @ToString:生成toString(), 默认情况下也会生成无参构造器。
    @NoArgsConstructor:lombok :注解, 会在编译时生成无参构造器。如果生成了其他构造器,默认的无参构造器就会被覆盖掉。所以还需要构造一个无参构造器。
    在这里插入图片描述
    在这里插入图片描述
  1. 演 示 使 用 Lombok 支 持 日 志 输 出 ( 建 议 使 用 slf4j), 修 改01_quickstart\src\main\java\com\hspedu\springboot\controller\HelloController.java

修改JavaBean Furn.java
在这里插入图片描述
修改HelloController.java

    @Autowired
    Furn furn;
    @RequestMapping("/furn")
    @ResponseBody
    public Furn furn() {
        // 普通写法
        log.info("furn=" + furn);
        // 占位写法
        log.info("furn={}", furn);
        return furn;
    }

打印成功!
在这里插入图片描述

6 Spring Initailizr

● Spring Initailizr 作用

  1. 程序员通过 Maven Archetype 来生成 Maven 项目,项目原型相对简陋, 需要手动配置, 比较灵活.
  2. 通过 Spring 官方提供的 Spring Initializr 来构建 Maven 项目,能完美支持 IDEA 和 Eclipse,
    让程序员来选择需要的开发场景(starter),还能自动生成启动类和单元测试代码。
  3. Spring Initailizr 对 Idea 版本有要求 同时还要走网络, 我自己还是习惯用, Maven Archetype 来生成 Maven 项目

6.2.1 需求: 使用 Spring Initailizr 创建 SpringBoot 项目,并支持 web 应用场景,支持 MyBatis

7 yaml

1、YAML 是"YAML Ain’t a Markup Language"(YAML 不是一种标记语言) 的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),是为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名

1、YAML 以数据做为中心,而不是以标记语言为重点
2、YAML 仍然是一种标记语言, 但是和传统的标记语言不一样, 是以数据为中心的标记语言.
3、YAML 非常适合用来做以数据为中心的配置文件 [springboot : application.yml]

7.2 使用文档

官方文档:
yaml for java(建议看这个)

7.3 yaml 基本语法

  1. 形式为 key: value;注意: 后面有空格
  2. 区分大小写
  3. 使用缩进表示层级关系
  4. 缩进不允许使用 tab,只允许空格 [有些地方也识别 tab , 推荐使用空格]
  5. 缩进的空格数不重要,只要相同层级的元素左对齐即可
  6. 字符串无需加引号
  7. yml 中, 注释使用 #

7.4 数据类型

7.4.1 字面量

  1. 字面量:单个的、不可再分的值。date、boolean、string、number、null
  2. 保存形式为 key: value 如图
    在这里插入图片描述

7.4.2 对象

  1. 对象:键值对的集合, 比如 map、hash、set、object
行内写法: k: {k1:v1,k2:v2,k3:v3}
monster: {id: 100,name: 牛魔王}

#或换行形式
k:
  k1: v1
  k2: v2
  k3: v3
monster:
  id: 100
  name: 牛魔王
  1. 举例说明
    在这里插入图片描述

7.4.3 数组

  1. 数组:一组按次序排列的值, 比如 array、list、queue
行内写法: k: [v1,v2,v3]
hobby: [打篮球, 打乒乓球, 踢足球]

#或者换行格式
k:
  - v1
  - v2
  - v3
hobby: 
  - 打篮球
  - 打乒乓球
  - 踢足球
  1. 举例说明
    在这里插入图片描述

7.5 yaml 应用实例

需求: 使用 yaml 配置文件 和 JavaBean 进行数据绑定, 体会 yaml

  1. 创建项目 configuration , 完成 yaml 的使用
  2. 运行效果

7.5.3 代码实现

  1. 创建一个新的 SpringBoot 项目 - configuration , 老韩使用灵活配置方式创建项目
  2. 在 pom.xml 引入 lombok, 并切换一下 springboot 版本
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>configuration</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>configuration</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>


  <!--导入SpringBoot父工程,规定的写法-->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.3</version>
  </parent>


  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <!--导入lombook-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>

    <!--  导入web项目场景启动器,会自动导入和web开发相关的所有依赖,非常方便-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

  </dependencies>
</project>

  1. 创建 02_configuration\src\main\java\com\hspedu\springboot\bean\Car.java , 老韩提醒一个小细节: 创建的 bean 需要在 SpringBootApplication 包或者其子包, 否则不会被默认扫描, 同时也不能完全使用 lombok 的相关简化注解.
    在这里插入图片描述
  2. 创建 02_configuration\src\main\java\com\hspedu\springboot\bean\Monster.java
package org.example.bean;

import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/1 20:31
 */
@Component
@Data
@ConfigurationProperties(prefix="monster")  // ConfigurationProperties 是关联application.yml中的数据
@ToString
public class Monster {
    private Integer id;
    private String name;
    private Integer age;
    private Boolean isMarried;
    private Date birth;
    private Car car;
    private String[] skill;
    private List<String> hobby;
    private Map<String, Object> wife;
    private Set<Double> salaries;
    private Map<String, List<Car>> cars;
}

  1. 创建 resources/application.yml, 演示各种写法
    @RestController:
    在这里插入图片描述
monster:
  id: 100
  name: 牛魔王
  age: 8989
  isMarried: true
  birth: 2000/12/5
  car: {name: 宝马, price: 9999}
  # skill: [芭蕉扇, 牛魔拳]
  skill:
    - 芭蕉扇
    - 牛魔拳
  hobby: [喝酒, 吃肉]
  wife: {key01: 玉面狐狸,key02: 铁扇公主}
  salaries:
    - 100.1
    - 200.2
  cars:
    grade01:
      - { name: 保时捷,price: 999999 }
      - name: 法拉利
        price: 888888.88
    grade02:
      - { name: 宝马,price: 200000 }
      - name: 奥迪
        price: 88888.3
  1. 创建 com/hspedu/springboot/controller/HiController.java
package org.example.controller;

import org.example.bean.Monster;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/1 21:27
 */
@RestController
public class HiController {
    @Autowired
    Monster monster;

    @RequestMapping("/monster")
    public Monster getMonster() {
        return monster;
    }
}

  1. 启动项目,完成测试
    在这里插入图片描述

7.6 yaml 使用细节

  1. 如 果 application.properties 和 application.yml 有 相 同 的 前 缀 值 绑 定 , 则application.properties 优先级高, 开发时,应当避免
  2. 字符串无需加引号, 这个在前面已经演示了, 如果你用" " 或者 ’ ’ 包起来, 也可以 , 简单举例
    在这里插入图片描述
  3. 解决 yaml 配置文件,不提示字段信息问题:在 pom.xml 加 入 spring-boot-configuration-processor 依 赖 , 可 以 从spring-boot-reference.pdf 拷贝(在"E:\BaiduSyncdisk\A研究生\分布式 微服务\分布式 微服务\SpringBoot\资料\spring-boot-reference.pdf")
<!-- 引入 yaml 文件提示, 可以看到 JavaBean 字段提示 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

  1. 老韩提示: 如果还没有提出提示, 可以安装一个 yaml 插件来搞定
    在这里插入图片描述

8 WEB 开发-静态资源访问

在线文档

8.2 基本介绍

  1. 只要静态资源放在类路径下: /static 、 /public 、 /resources 、 /META-INF/resources 可以被直接访问- 对应文件 WebProperties.java
    类路径如何查看:进入WebProperties.java 中查看
    在这里插入图片描述
  2. 常见静态资源:JS、CSS 、图片(.jpg .png .gif .bmp .svg)、字体文件(Fonts)等
  3. 访问方式 :默认: 项目根路径/ + 静态资源名 比如 http://localhost:8080/hi.jpg . - 设置WebMvcProperties.java
private String staticPathPattern = "/**";

8.3 快速入门

  1. 创建 SpringBoot 项目 springbootweb , 老师使用灵活配置方式来创建项目
  2. 创建相关静态资源目录, 并放入测试图片, 没有目录,自己创建即可, 完成测试
    在这里插入图片描述

8.4 静态资源访问注意事项和细节

  1. 静态资源访问原理:静态映射是 /**, 也就是对所有请求拦截,请求进来,先看 Controller能不能处理,不能处理的请求交给静态资源处理器,如果静态资源找不到则响应 404 页面
    在这里插入图片描述

  2. 改变静态资源访问前缀,比如我们希望 http://localhost:8080/hspres/* 去请求静态资源,应用场景:静态资源访问前缀和控制器请求路径冲突

    1. 创建 D:\idea_java_projects\springboot2\03_web\src\main\resources\application.yml
    spring:
    	mvc:
    	static-path-pattern: /hspres/**		# /hspres 是加的前缀,/**表示拦截所有请求
    
    1. 重启应用,完成测试, 浏览器输入: http://localhost:8080/hspres/1.jpg
  3. 改变默认的静态资源路径,比如希望在类路径下增加 hspimg 目录 作为静态资源路径 ,并完成测试
    在这里插入图片描述
    2) 配置 springboot2\03_web\src\main\resources\application.yml, 增加路径

    spring:
    	mvc:
    	static-path-pattern: /hspres/**
    web:
    	resources:
    		# 修改/指定静态资源的访问路径
    		static-locations: [classpath:/hspimg/, classpath:/public/, classpath:/static/]
    

图片的意思是把原来的文件里的路径再加上,否则原来的路径此时再访问就访问不到了。在这里插入图片描述

9 Rest 风格请求处理

  1. Rest 风格支持(使用 HTTP 请求方式动词来表示对资源的操作)
  2. 举例说明:
    ● 请求方式: /monster
    ● GET-获取怪物
    ● DELETE-删除怪物
    ● PUT-修改怪物
    ● POST-保存妖怪

9.2 SpringBoot Rest 风格应用实例

需求: 演示 SpringBoot 中如何实现 Rest 风格的 增删改查
在这里插入图片描述

  1. 创建 com/hspedu/web/controller/MonsterController.java
package org.example.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 20:14
 */
@RestController
public class MonsterController {

    // @RequestMapping(value = "/monster", method = RequestMethod.GET)
    @GetMapping(value = "/monster")
    public String getMonster() {
        return "GET-查询妖怪";
    }


    // 等价于
    // @RequestMapping(value = "/monster", method = RequestMethod.POST)
    @PostMapping("/monster")
    public String saveMonster() {
        return "POST-添加妖怪";
    }

    @PutMapping(value = "/monster")
    public String putMonster() {
        return "PUT-修改妖怪";
    }

    @DeleteMapping("/monster")
    public String deleteMonster() {
        return "DELETE-删除妖怪";
    }

}

  1. 使用 Postman 完成测试, 请求 url: http://localhost:8080/monster
    在这里插入图片描述
    在这里插入图片描述

Rest 风格请求 -注意事项和细节

1、客户端是 PostMan 可以直接发送 Put、delete 等方式请求,可不设置 Filter
2、如果要 SpringBoot 支持 页面表单的 Rest 功能, 则需要注意如下细节
-----1) Rest 风 格 请 求 核 心 Filter ; HiddenHttpMethodFilter , 表 单 请 求 会 被HiddenHttpMethodFilter 拦截 , 获取到表单 _method 的值, 再判断是 PUT/DELETE/PATCH(老师注释: PATCH 方法是新引入的,是对 PUT 方法的补充,用来对已知资源进行局部更新:https://segmentfault.com/q/1010000005685904)
----2) 如果要 SpringBoot 支持 页面表单的 Rest 功能, 需要在 application.yml 启用 filter 功能,否则无效
----3) 修改 application.yml 启用 filter 功能

spring:
	mvc:
		static-path-pattern: /hspres/**
		hiddenmethod:
			filter:
				enabled: true #开启页面表单的 Rest 功能
	web:
		resources:
		static-locations: [classpath:/hspimg/, classpath:/public/, classpath:/static/]

----- 4) 修改对应的页面, 同学们自己测即可.

  • 创建 D:\hsp_springboot_temp\03-web\src\main\resources\public\rest.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>rest</title>
</head>
<body>
<h1>测试 rest 风格的 url, 来完成请求.</h1>
<form action="/monster" method="post">
    u: <input type="text" name="name"><br/>
    <!--如果要测试 delete, put , 就打开下面的注释-->
    <!-- <input type="hidden" name="_method" value="delete">-->
    <input type="submit" value="点击提交">
</form>
</body>
</html>

9.3 思考题

  1. 留一个思考题: 为什么这里 return “GET-查询妖怪”, 返回的是字符串, 而不是转发到对应的资源文件?-在 SpringMVC

答:因为@ResController 是一个复合注解, 含有@ResponseBody, 所以springboot 底层(springmvc), 在处理return “xxx” 时, 会以@ResponseBody 注解进行解析处理, 即返回Json格式的字符串 “xxx”, 而不会使用视图解析器来处理。如果我们把 @RestController 改成 @Controller , 当你访问getMonster() 时, 如果你有 xxx.html就会转发到 xxx.html , 如果没有 xxx.html , 就会报 404。
提示: 在测试时, 将 xxx.html 放在 main\resources\public\xxx.html 进行测试, 并在application.yml 配置视图解析器

10 接收参数相关注解

10.1 基本介绍

  1. SpringBoot 接收客户端提交数据/参数会使用到相关注解
  2. 详 解 @PathVariable 、 @RequestHeader 、 @ModelAttribute 、 @RequestParam 、@MatrixVariable、@CookieValue、@RequestBody

10.2 接收参数相关注解应用实例

需求: 演示各种方式提交数据/参数给服务器,服务器如何使用注解接收
在这里插入图片描述

  1. 创建 03_web\src\main\resources\public\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>hello, 韩顺平教育</h1>
基本注解:
<hr/>
<a href="monster/100/king">@PathVariable-路径变量 monster/100/king</a><br/><br/>
</body>
</html>
  1. 演示@PathVariable 使用,创建 com/hspedu/web/controller/ParameterController.java ,完成测试
  2. 演示@RequestHeader 使用,修改 ParameterController.java , 完成测试
    @PathVariable 是用于当传入变量名和函数变量名不同时,映射的
    @RequestHeader 是用于得到 Http请求头 的
package org.example.controller;

import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 21:24
 */
@RestController
public class ParameterController{

    /*
    /monster/{id}/{name} 解读
    1. /monster/{id}/{name} 构成完整请求路径
    2. {id} {name} 就是占位变量
    3. @PathVariable("name") 这里的name 和{name} 命名保持一致
    4. String name_ 表示自定义名字。故意不同以示区别
    5. @PathVariable Map<String, String> map 把所有传递的值传入map
    */
    @GetMapping("/monster/{id}/{name}")
    public String pathVariable(@PathVariable("id") Integer id,
                               @PathVariable("name") String name_,
                               @PathVariable Map<String, String> map,
                               @RequestHeader("Host") String host,
                               @RequestHeader Map<String, String> header) {
        System.out.println("id= " + id + " name= " + name_);
        System.out.println(map);
        System.out.println("===============================");
        System.out.println("host= " + host);
        System.out.println(header);
        return "success";
    }
}

  1. 完成测试
    在这里插入图片描述
    IDEA:
    在这里插入图片描述

  2. 演示@RequestHeader 使用,修改 ParameterController.java , 完成测试
    @RequestHeader 是用于得到 Http请求头 的

修改ParameterController.java,增加下面代码。

    @GetMapping("/requestHeader")
    public String requestHeader(
            @RequestHeader("Host") String host,
            @RequestHeader Map<String, String> header
            ) {
        System.out.println("===============================");
        System.out.println("host= " + host);
        System.out.println(header);
        return "success";
    }
  1. 演示@RequestParam 使用,修改 ParameterController.java , 完成测试
    在这里插入图片描述

√ 修改 index.html

<a href="hi?name= 韩 顺 平 &fruit=apple&fruit=pear">@RequestParam- 获 取 请 求 参 数</a><br/><br/>

√ 修改 ParameterController.java,增加如下代码:

    /**
     * 如果 fruit 是多个值, 可以使用 List 来接收
     */
    @GetMapping("/hi")
    public String hi(
            @RequestParam("name") String name,
            @RequestParam("fruit") List<String> fruit,
            @RequestParam Map<String, String> paras) {
        System.out.println("===============================");
        System.out.println("name= " + name);
        System.out.println(fruit);
        System.out.println(paras);
        return "success";
    }

测试:
点击
在这里插入图片描述
得到:
在这里插入图片描述

  1. 演示@CookieValue 使用,修改 ParameterController.java , 完成测试
    √ 修改 index.html
<a href="cookie">@CookieValue-获取 cookie 值</a><br/><br/>

√ 修改 ParameterController.java

    /**
     * 如果要测试,可以先写一个方法,在浏览器创建对应的 cookie
     * 说明 1. value = "cookie_key" 表示接收名字为 cookie_key 的 cookie
     * 2. 如果浏览器携带来对应的 cookie , 那么 后面的参数是 String ,则接收到的是对应对 value
     * 3. 后面的参数是 Cookie ,则接收到的是封装好的对应的 cookie
     */
    @GetMapping("/cookie")
    public String cookie(
            @CookieValue(value = "cookie_key", required = false) String cookie_value,
            @CookieValue(value = "username", required = false) Cookie cookie) {
        System.out.println("username=" + cookie.getName() + "--" + cookie.getValue());
        return "success";
    }
  1. 演示@RequestBody 使用,修改 ParameterController.java , 完成测试
    @RequestBody 将客户端提交的 json 数据,封装成 JavaBean 对象
    √ 修改 index.html
<hr/>
<h1>测试@RequestBody 获取数据: 获取 POST 请求体</h1>
<form action="/save" method="post">
    姓名: <input name="name"/> <br>
    年龄: <input name="age"/> <br/>
    <input type="submit" value="提交"/>
</form>

√ 修改 ParameterController.java

/**
* @RequestBody 是整体取出 Post 请求内容
*/
@PostMapping("/save")
public String postMethod(@RequestBody String content){
System.out.println("content= " + content);
return "success";
}

测试:
在这里插入图片描述在这里插入图片描述
7. 演示@RequestAttribute 使用,创建 com/hspedu/web/controller/RequestController.java ,完成测试
√ 修改 index.html

<a href="login">@RequestAttribute-获取 request 域属性-</a>

√ 创建 RequestController.java

package org.example.controller;

import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 22:08
 */
@Controller
public class RequestController {
    @GetMapping("/login")
    public String login(HttpServletRequest request) {
        request.setAttribute("user", "LGQ");
        return "forward:/ok";   // 服务器内部转发到ok。对比的是 重定向
    }

    @ResponseBody
    @GetMapping("/ok")
    public String ok(@RequestAttribute(value = "user", required = false) String user,
                     HttpServletRequest request) {
        System.out.println("request域中的user=" + user);
        System.out.println("request域中的user=" + request.getAttribute("user"));
        return "success";
    }
}

测试:
在这里插入图片描述
在这里插入图片描述

10.3 复杂参数

  1. 在开发中,SpringBoot 在响应客户端请求时,也支持复杂参数
  2. Map、Model、Errors/BindingResult、RedirectAttributes、ServletResponse、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder、HttpSession
  3. Map、Model 数据会被放在 request 域, 底层 request.setAttribute()。Model
  4. RedirectAttributes 重定向携带数据

复杂参数应用实例

  1. 修改 com/hspedu/web/controller/RequestController.java
package org.example.controller;

import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 22:08
 */
@Controller
public class RequestController {
    @GetMapping("/login")
    public String login(HttpServletRequest request) {
        request.setAttribute("user", "LGQ");
        return "forward:/ok";   // 转发到ok
    }

    @ResponseBody
    @GetMapping("/ok")
    public String ok(@RequestAttribute(value = "user", required = false) String user,
                     HttpServletRequest request) {
        System.out.println("request域中的user=" + user);
        System.out.println("request域中的user=" + request.getAttribute("user"));
        return "success";
    }

    @GetMapping("/register")
    public String register(Map<String, Object> map,
                           Model model,
                           HttpServletResponse response) {
        map.put("user", "lgq");
        map.put("job", "穷学生");
        model.addAttribute("sal", 999999.9);
        // 可以将cookie加入到response对象,返回给客户端
        Cookie cookie = new Cookie("pwd", "487563");
        response.addCookie(cookie);
        return "forward:/registerOk";
    }

    @ResponseBody
    @GetMapping("/registerOk")
    public String registerOk(HttpServletRequest request) {
        System.out.println("request 域中 user= " + request.getAttribute("user"));
        System.out.println("request 域中 job= " + request.getAttribute("job"));
        System.out.println("request 域中 sal= " + request.getAttribute("sal"));
        return "success";
    }
}

  1. 完成测试 : 浏览器 http://localhost:8080/register
    在这里插入图片描述

register方法中为什么用HttpServletResponse,registerOk方法中为什么用HttpServletRequest?

HttpServletResponse 用于修改到客户端的响应。对其进行修改,最终呈现的效果都是在客户端上

HttpServletRequest修改的是请求,这个请求是到服务器(Tomcat)的请求。这里是在register方法中添加一些属性(通过Map和Model),然后在registerOk方法中取出属性。

10.4 自定义对象参数-自动封装

  1. 在开发中,SpringBoot 在响应客户端请求时,也支持自定义对象参数
  2. 完成自动类型转换与格式化
  3. 支持级联封装

10.4.2 自定义对象参数-应用实例

: 演示自定义对象参数使用,完成自动封装,类型转换

  1. 创建 public/save.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加妖怪</title>
</head>
<body>
<h1>添加妖怪-坐骑[测试封装 POJO;]</h1>
<form action="/savemonster" method="post">
    编号: <input name="id" value="100"><br/>
    姓名: <input name="name" value="牛魔王"/> <br/>
    年龄: <input name="age" value="120"/> <br/>
    婚否: <input name="isMarried" value="true"/> <br/>
    生日: <input name="birth" value="2000/11/11"/> <br/>
    坐骑:<input name="car.name" value="法拉利"/><br/>
    价格:<input name="car.price" value="99999.9"/>
    <input type="submit" value="保存"/>
</form>
</body>
</html>
  1. 创 建 03_web\src\main\java\com\hspedu\web\bean\Car.java 和03_web\src\main\java\com\hspedu\web\bean\Monster.java
package org.example.bean;

import lombok.Data;
import lombok.ToString;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 22:44
 */
@ToString
@Data
public class Car {
    private String name;
    private Double price;
}



package org.example.bean;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 20:15
 */

import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Component
@Data
@ConfigurationProperties(prefix="monster")  // ConfigurationProperties 是关联application.yml中的数据
@ToString
public class Monster {
    private Integer id;
    private String name;
    private Integer age;
    private Boolean isMarried;
    private Date birth;
    private Car car;
    // private String[] skill;
    // private List<String> hobby;
    // private Map<String, Object> wife;
    // private Set<Double> salaries;
}

  1. 修改 com/hspedu/web/controller/ParameterController.java 增加处理添加请求
    @PostMapping("/savemonster")
    public String saveMonster(Monster monster) {
        System.out.println("monster=" + monster);
        return "success";
    }
  1. 完成测试, 浏览器 http://localhost:8080/save.html
    在这里插入图片描述在这里插入图片描述

11 自定义转换器

11.1 基本介绍

  1. SpringBoot 在响应客户端请求时,将提交的数据封装成对象时,使用了内置的转换器
  2. SpringBoot 也支持自定义转换器, 这个内置转换器在 debug 的时候, 可以看到, 后面给小伙伴演示, 提供了 124 个内置转换器. 看下源码 GenericConverter-ConvertiblePair
    在这里插入图片描述

11.2 自定义转换器-应用实例

1 需求说明 : 演示自定义转换器使用
在这里插入图片描述

  1. 修改 save.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加妖怪</title>
</head>
<body>
<h1>添加妖怪-坐骑[测试封装 POJO;]</h1>
<form action="/savemonster" method="post">
    编号: <input name="id" value="100"><br/>
    姓名: <input name="name" value="牛魔王"/> <br/>
    年龄: <input name="age" value="120"/> <br/>
    婚否: <input name="isMarried" value="true"/> <br/>
    生日: <input name="birth" value="2000/11/11"/> <br/>
    <!--    坐骑:<input name="car.name" value="法拉利"/><br/>-->
    <!--    价格:<input name="car.price" value="99999.9"/>-->
    
    <!-- 使用自定义转换器关联 car, 字符串整体提交 -->
    坐骑: <input name="car" value="保时捷,66666.6">
    <input type="submit" value="保存"/>
</form>
</body>
</html>
  1. 创 建D:\hsp_springboot_temp\03-web\src\main\java\com\hspedu\web\config\WebConfig.java,增加自定义转换器-

过,没学

13 内容协商

  1. 根据客户端接收能力不同,SpringBoot 返回不同媒体类型的数据
  2. 比如: 客户端 Http 请求 Accept: application/xml 则返回 xml 数据,客户端 Http 请求Accept: application/json 则返回 json 数据
    在这里插入图片描述

13.2 内容协商-应用实例

● 需求说明 : 使用Postman发送Http请求,根据请求头不同,返回对应的json数据 或者 xml 数据 , 如图

  1. 在 pom.xml 增加处理 xml 的依赖
    <!--导入支持返回xml数据格式-->
    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
  1. 使用Postman发出不同的Http Header , 可以看到返回对应的数据格式
    在这里插入图片描述
    在这里插入图片描述

  2. 使用浏览器请求,为什么会返回 xml 数据分析,而不是 json?
    在这里插入图片描述

13.3 注意事项和使用细节

  1. Postman 可以通过修改 Accept 的值,来返回不同的数据格式
  2. 对于浏览器,我们无法修改其 Accept 的值,怎么办? 解决方案: 开启支持基于请求参数的内容协商功能
    1. 修改 application.yml, 开启基于请求参数的内容协商功能
    spring:
      mvc:
        hiddenmethod:
          filter:
            enabled: true # 开启页面表单的Rest功能。
        contentnegotiation:
          favor-parameter: true # 开启基于请求参数的内容协商功能
      web:
       resources:
         static-locations: [classpath:/public/, classpath:/static/]
    
    
  3. 浏览器访问:
    http://localhost:8080/monster?format=xml
    在这里插入图片描述
    json格式:
    在这里插入图片描述
  1. 注意,参数 format 是规定好的 , 在开启请求参数的内容协商功能后,SpringBoot 底层ParameterContentNegotiationStrategy 会通过 format 来接收参数,然后返回对应的媒体类型/数据格式 , 当然 format=xx 这个 xx 媒体类型/数据格式 是 SpringBoot 可以处理的才行,不能乱写.

14 Thymeleaf(感觉过时 ) 在复习看这里

在线文档:

基本介绍

  1. Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,可完全替代 JSP
  2. Thymeleaf 是一个 java 类库,他是一个 xml/xhtml/html5 的模板引擎,可以作为 mvc 的 web 应用的 view 层

● Thymeleaf 的优点

  1. 实现 JSTL、 OGNL 表达式效果, 语法相似, java 程序员上手快
  2. Thymeleaf 模版页面无需服务器渲染,也可以被浏览器运行,页面简洁。
  3. SpringBoot 支持 FreeMarker、Thymeleaf、veocity 。

● Thymeleaf 的缺点

  1. 缺点: 并不是一个高性能的引擎,适用于单体应用
  2. 老韩说明:如果要做一个高并发的应用, 选择前后端分离更好,但是作为 SpringBoot 推荐的模板引擎,老师还是要讲解 Thymeleaf 使用, 这样小伙伴在工作中使用到, 也能搞定
  3. 后面老韩还要讲 Vue + ElementPlus + Axios + SpringBoot 前后端分离

14.3 Thymeleaf 机制说明

  1. Thymeleaf 是服务器渲染技术, 页面数据是在服务端进行渲染的
  2. 比如: manage.html 中一段 thymeleaf 代码, 是在用户请求该页面时,有 thymeleaf 模板引擎完成处理的(在服务端完成), 并将结果页面返回.
    在这里插入图片描述
  3. 因此使用了 Thymeleaf , 并不是前后端分离.

14.4 Thymeleaf 语法

14.4.1 表达式

在这里插入图片描述
2. 字面量
文本值: ‘hsp edu’ , ‘hello’ ,…数字: 10 , 7 , 36.8 , …布尔值: true , false
空值: null
变量: name,age,… 变量不能有空格

3. 文本操作
文本操作
字符串拼接: +
变量替换: |age= ${age}|

14.4.2 运算符

  1. 数学运算:运算符: + , - , * , / , %
  2. 布尔运算
    运算符: and , or
    一元运算: ! , not
  3. 比较运算
    比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )
  4. 条件运算
    If-then: (if) ? (then)
    If-then-else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)

14.4.3 th 属性

html 有的属性,Thymeleaf 基本都有,而常用的属性大概有七八个。其中 th 属性执行的优先级从 1~8,数字越低优先级越高
● th:text :设置当前元素的文本内容,相同功能的还有 th:utext,两者的区别在于前者不会转义 html 标签,后者会。优先级不高:order=7
● th:value:设置当前元素的 value 值,类似修改指定属性的还有 th:src,th:href。优先级不高:order=6
● th:each:遍历循环元素,和 th:text 或 th:value 一起使用。注意该属性修饰的标签位置,详细往后看。优先级很高:order=2
● th:if:条件判断,类似的还有 th:unless,th:switch,th:case。优先级较高:order=3
● th:insert:代码块引入,类似的还有 th:replace,th:include,三者的区别较大,若使用不恰当会破坏 html 结构,常用于公共代码块提取的场景。优先级最高:order=1
● th:fragment:定义代码块,方便被 th:insert 引用。优先级最低:order=8
● th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4
● th:attr:修改任意属性,实际开发中用的较少,因为有丰富的其他 th 属性帮忙,类似的还有 th:attrappend,th:attrprepend。优先级一般:order=5

14.5 Thymeleaf 综合案例

在这里插入图片描述
在这里插入图片描述

思路分析

在这里插入图片描述

  1. 创建项目, 项目名使用 04-springboot-usersys, 老韩还是使用灵活创建项目的方式.
  2. 说明一下,要支持 Thymeleaf, 需要加入 thymeleaf-starter, 在 pom.xml 配置
    <!--导入thymeleaf-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

  1. 创建 adminLogin.html 和 manage.html 和静态图片到指定目录,从准备好的拷贝即可, 注意
    我将 html 文件放到 templates/ 目录下, 该目录, 不能直接访问.
    在这里插入图片描述
adminLogin.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
    <h1>用户登陆</h1>
    <form action="#" method="post" th:action="@{/login}"
    >
        <label style="color: red" th:text="${msg}"></label><br/>
        用户名:<input type="text" style="width:150px" name="name"/><br/><br/>
        密 码:<input type="password" style="width:150px" name="password"/><br/><br/>
        <input type="submit" value="登录"/>
        <input type="reset" value="重新填写"/>
    </form>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>

==============manage.html========================
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>管理后台</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<a href='#'>返回管理界面</a>  <a href='#' th:href="@{/}">安全退出</a>   欢迎您:[[${session.loginAdmin.name}]]
<hr/>
<div style="text-align: center">
    <h1>管理雇员~</h1>
    <table border="1px" cellspacing="0" bordercolor="green" style="width:800px;margin: auto">
        <tr bgcolor="pink">
            <td>id</td>
            <td>name</td>
            <td>pwd</td>
            <td>email</td>
            <td>age</td>
        </tr>
        <tr bgcolor="#ffc0cb" th:each="user:${users}">
            <td th:text="${user.id}">a</td>
            <td th:text="${user.name}">b</td>
            <td th:text="${user.password}">c</td>
            <td th:text="${user.email}">d</td>
            <td th:text="${user.age}">e</td>
        </tr>
    </table>
    <br/>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
  1. 创建 com/hspedu/usersys/bean/Admin.java
package org.example.bean;

import lombok.Data;

/**
 * @author 韩顺平
 * @version 1.0
 */
@Data
public class Admin {
    private String name;
    private String password;
}

  1. 创建 com/hspedu/usersys/bean/User.java
package org.example.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 韩顺平
 * @version 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String password;
    private Integer age;
    private String email;
}

  1. 创建 com/hspedu/usersys/controller/IndexController.java 默认进入登录页面
package org.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/4 19:39
 * 这是默认登陆界面。由于不能直接访问thymeleaf下的login.html文件,所以写这一个controller,然后经过请求转发到thymeleaf下。
 */
@Controller
public class IndexController {

    // 编写方法,请求转发 到登陆页面
    @GetMapping(value = {"/", "/login"})
    public String login() {
        /*
        * 因为这里引入了 starter-thymeleaf
        * 这里会直接使用视图解析器到 thymeleaf 下的模板文件adminLogin.html
        * 直接访问templates下的文件不能直接访问到。
        * */
        return "adminLogin";
    }
}

  1. 创建 com/hspedu/usersys/controller/AdminController.java 处理登录请求 完成测试
package org.example.controller;

import lombok.extern.slf4j.Slf4j;
import org.example.bean.Admin;
import org.example.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.jws.WebParam;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;


/**
 * @Author: GQLiu
 * @DATE: 2024/5/4 19:47
 * 处理登陆请求
 */
@Controller
@Slf4j
public class AdminController {
    // 响应用户的登陆请求
    @PostMapping("/login")  // 登陆的请求会以post方式打到这里
    public String login(Admin admin, HttpSession httpSession, Model model) {
        // 验证用户登录是否合法
        if(StringUtils.hasText(admin.getName()) && "666".equals(admin.getPassword())) {
            // 将登陆用户保存到session
            httpSession.setAttribute("loginAdmin", admin);

            // 重定向到mamage.html, 不使用请求转发是防止用户刷新页面会重复提交
            // return "forward:/manage.html";
            return "redirect:/manage.html";
        } else {
            // 不合法,就重新登陆。
            model.addAttribute("msg", "账号/用户错误");
            return "adminLogin";
        }
    }

    // 处理用户 的请求 manage.html
    @GetMapping("/manage.html")
    public String mainPage(Model model, HttpSession httpSession) {
        //这里老师暂时使用在方法验证,后面我们统一使用拦截器来验证

        log.info("进入mainPage()");
        //可以这里集合-模拟用户数据, 放入到request域中,并显示
        ArrayList<User> users = new ArrayList<>();
        users.add(new User(1, "关羽~", "666666", 20, "gy@sohu.com"));
        users.add(new User(2, "张飞", "666666", 30, "zf@sohu.com"));
        users.add(new User(3, "赵云", "666666", 22, "zy@sohu.com"));
        users.add(new User(4, "马超", "666666", 28, "mc@sohu.com"));
        users.add(new User(5, "黄忠", "666666", 50, "hz@sohu.com"));

        //将数据放入到request域
        model.addAttribute("users", users);

        return "manage"; //这里才是我们的视图解析到 /templates/manage.html

    }
}

//浏览器输入: http://localhost:8080/login

在这里插入图片描述

15 拦截器-HandlerInterceptor

15.1 基本介绍

  1. 在 Spring Boot 项目中, 拦截器是开发中常用手段,要来做登陆验证、性能检查、日志记录等。
  2. 基本步骤:
√ 编写一个拦截器实现 HandlerInterceptor 接口

√ 拦截器注册到配置类中(实现 WebMvcConfigurer 的 addInterceptors)

√ 指定拦截规则

√ 回顾 SpringMVC 中讲解的 Interceptor

  1. 创建 com/hspedu/usersys/interceptor/LoginInterceptor.java

import lombok.extern.slf4j.Slf4j;
        import org.springframework.web.servlet.HandlerInterceptor;
        import org.springframework.web.servlet.ModelAndView;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import javax.servlet.http.HttpSession;
/**
 * @author 韩顺平
 * @version 1.0
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行之前*/
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("preHandle 拦截的请求路径是{}", requestURI);
        //登录检查逻辑
        HttpSession session = request.getSession();
        Object loginAdmin = session.getAttribute("loginAdmin");
        if (loginAdmin != null) {
        //放行
            return true;
        }
        //拦截
        request.setAttribute("msg", "错误/重新登录");
        request.getRequestDispatcher("/").forward(request, response);
        return false;
    }
    /**
     * 目标方法执行完成以后*/
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle 执行");
    }
    /**
     * 页面渲染以后*/
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {
        log.info("afterCompletion 执行");
    }
}
  1. 创建 com/hspedu/usersys/config/WebConfig.java
import com.hspedu.usersys.interceptor.LoginInterceptor;
// import org.apache.catalina.connector.Connector;
// import org.apache.coyote.http11.Http11NioProtocol;
// import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
// import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 这里实现 Spring-Boot 定制功能, 加入自己的配置
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**") //拦截所有请求
                .excludePathPatterns("/", "/login", "/images/**"); //放行的请求, 可以根据需要增加
    }
}

15.2.3 注意事项和细节

1、URI 和 URL 的区别
URI = Universal Resource Identifier
URL = Universal Resource Locator
Identifier:标识符,Locator:定位器 从字面上来看, URI 可以唯一标识一个资源, URL 可以提供找到该资源的路径

String requestURI = request.getRequestURI();
String requestURL = request.getRequestURL().toString();

2、注册拦截器, 依然可以使用如下方式二:

@Configuration
public class WebConfig /*implements WebMvcConfigurer*/ {
    //将我们的拦截器, 注入到容器中
//@Override
//public void addInterceptors(InterceptorRegistry registry) {
// System.out.println("addInterceptors...");
// //加入我们的拦截器
// registry.addInterceptor(new LoginInterceptor())
// .addPathPatterns("/**") //拦截所有请求
// .excludePathPatterns("/","/login","/images/**");//
//}
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                System.out.println("addInterceptors...~~~~:):)");
				//加入我们的拦截器
                registry.addInterceptor(new LoginInterceptor())
                        .addPathPatterns("/**") //拦截所有请求
                        .excludePathPatterns("/", "/login", "/images/**");//
            }
        };
    }
}

16 文件上传

16.1 应用实例

● 需求: 演示 Spring-Boot 通过表单注册用户,并支持上传图片

● 代码实现-文件上传

  1. 创建 SpringBootProjects/springbootweb/src/main/resources/public/upload.html , 要求头像只能选择一个, 而宠物可以上传多个图片
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>upload</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
    <h1>注册用户~</h1>
    <form action="#" method="post" th:action="@{/upload}" enctype="multipart/form-data">
        用户名:<input type="text" style="width:150px" name="name"/><br/><br/>
        电 邮:<input type="text" style="width:150px" name="email"/><br/><br/>
        年 龄:<input type="text" style="width:150px" name="age"/><br/><br/>
        职 位:<input type="text" style="width:150px" name="job"/><br/><br/>
        头 像:<input type="file" style="width:150px" name="header"><br/><br/>
        宠 物:<input type="file" style="width:150px" name="photos" multiple><br/><br/>
        <input type="submit" value="注册"/>
        <input type="reset" value="重新填写"/>
    </form>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
  1. 编写UploadController.java。
package org.example.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/3 15:02
 */
@Controller
@Slf4j
public class UploadController {
    // @GetMapping("/upload.html")
    // public String uploadPage() {
    //     System.out.println("进入upload.html");
    //     return "upload";
    // }

    @ResponseBody
    @PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("name")  String name,
                         @RequestParam("age")   Integer age,
                         @RequestParam("job")   String job,
                         @RequestPart("header") MultipartFile header,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {
        log.info("上传的信息:email={},name={},age={}, job={}, header={},photos={}", email, name, age, job, header.getSize(), photos.length);
        // // 自己动态创建文件夹来存放文件。例如在 resources/static/images/upload
        // String path = ResourceUtils.getURL("classpath:").getPath();
        // File file = new File(path + "static/images/upload/");
        // if(!file.exists()) file.mkdirs();

        if(!header.isEmpty()) {
            // 保存到文件服务器或OSS服务器/需要先创建好目录f:\\temp_upload
            String originalFilename = header.getOriginalFilename();
            // 方式 1: 指定某个目录存放上传文件
            header.transferTo(new File("f:\\temp_upload\\" + originalFilename));
            // // 方式 2: 动态创建目录存放文件
            // header.transferTo(new File(file.getAbsolutePath() + "/" + originalFilename));
        }

        if(photos.length > 0) {
            for (MultipartFile photo : photos) {
                if(!photo.isEmpty()) {
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("f:\\temp_upload\\" + originalFilename));
                    // //动态创建文件夹
                    // photo.transferTo(new File(file.getAbsolutePath() + "/" + originalFilename));
                }
            }
        }
        return "上传成功~";
    }
}

  1. 在浏览器中输入http://localhost:8080/upload 访问,输入数据
    在这里插入图片描述
    后台即可得到对应的输入的内容:在这里插入图片描述
    同时在磁盘中得到对应上传的文件
    在这里插入图片描述

拓展

1、解决文件覆盖问题, 如果文件名相同, 会出现覆盖问题, 如何解决
在这里插入图片描述
2、解决文件分目录存放问题, 如果将文件都上传到一个目录下,当上传文件很多时,会造成访问文件速度变慢,因此 可以将文件上传到不同目录 比如 一天上传的文件,统一放到一个文件夹 年/月/日, 比如 2022/11/11 目录

17 异常处理

  1. 默认情况下,Spring Boot 提供/error 处理所有错误的映射
  2. 对于机器客户端,它将生成 JSON 响应,其中包含错误,HTTP 状态和异常消息的详细信息。对于浏览器客户端,响应一个"whitelabel"错误视图,以 HTML 格式呈现相同的数据
    在这里插入图片描述

17.2 拦截器 VS 过滤器

1、使用范围不同

  1. 过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在 Servlet 规范中定义的,也就是说过滤器 Filter 的使用要依赖于 Tomcat 等容器,Filter 只能在 web 程序中使用
  2. 拦截器(Interceptor) 它是一个 Spring 组件,并由 Spring 容器管理,并不依赖 Tomcat 等容器,是可以单独使用的。不仅能应用在 web 程序中,也可以用于 Application 等程序中

2、过滤器 和 拦截器的触发时机也不同,看下边这张图
首先经过猫猫,然后再经过过滤器。如果过滤器通过,就再进入servlet。如果再通过,就进入拦截器。如果再通过,则进入最终的处理的controller。如果controller返回成功,就返回最终结果。如果返回不成功,就去走自定义异常或异常的那条线。
在这里插入图片描述

  1. 过滤器 Filter 是在请求进入容器后, 但在进入 servlet 之前进行预处理, 请求结束是在servlet 处理完以后
  2. 拦截器 Interceptor 是在请求进入 servlet 后, 在进入 Controller 之前进行预处理的,Controller 中渲染了对应的视图之后请求结束
  1. 说明: 过滤器不会处理请求转发, 拦截器会处理请求转发
  2. 至于过滤器和拦截器的原理和机制, 老韩已经详细讲解过了, 过滤器在 JavaWeb 讲过, 拦
    截器在 SpringMVC 讲过,

17.3 自定义异常页面

17.3.1 文 档 :

17.3.2 自定义异常页面说明

如何找到这个文档位置, 看下面一步步的指引
https://docs.spring.io/spring-boot/docs/current/reference/html/index.html => a single page html => 8.web => servlet web application => The “Spring Web MVC Framework” => Error Handling => Custom Error Pages
在这里插入图片描述

17.3.3 自定义异常页面-应用实例

需求: 自定义 404.html 500.html 4xx.html 5xx.html 当发生相应错误时,显示自定义的页面信息

  1. 创建 4 个页面, 这几个页面拷贝即可。随便自己怎么写里面的东西都可以。
    在这里插入图片描述
  2. 创建 com/hspedu/usersys/controller/MyErrorController.java , 用于模拟错误
package org.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/3 21:57
 */
@Controller
public class MyErrorController {
    // 模拟一个服务器内部错误 500
    @GetMapping(value = {"/err"})
    public String err() {
        int i = 10 / 0;
        return "manage";
    }

    // 用Get请求err2(err2要以Post方式请求)
    @PostMapping(value = {"/err2"})
    public String err2() {
        return "manage";
    }
}

  1. 完成测试
    ● 需要先登录,再进行测试,否则会被拦截器打回登录页面
    ● 对于 /err2 , 使用 get 方式去请求,就会生成 400 错误, 可以看到 4xx.html
    在这里插入图片描述
    在这里插入图片描述

17.4 全局异常

说明

  1. @ControllerAdvice+@ExceptionHandler 处理全局异常
  2. 底层是 ExceptionHandlerExceptionResolver 支持的
  3. 全局异常是处理Java内部的错误导致的服务器的错误,当这些错误发生时,指定到哪些页面。

全局异常-应用实例
需求: 演示全局异常使用, 当发生 ArithmeticException、NullPointerException 时,不使用默认异常机制匹配的 xxx.html , 而是显示全局异常机制指定的错误页面

  1. 创建 com/hspedu/usersys/exception/GlobalExceptionHandler.java
package org.example.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;

import javax.jws.WebParam;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/5 10:54
 */
@Slf4j
// @ControllerAdvice
public class GlobalExceptionHandler {
    // 可以处理多个异常
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String HandleAriException(Exception e ,Model model){
        log.error("异常信息为{}", e);
        model.addAttribute("msg", e);
        return "/error/global";
    }
}

  1. 创建 templates/error/global.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>全局异常</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
  <h1>全局异常/错误 发生了:)</h1><br/>
  异常/错误信息: <h1 th:text="${msg}"></h1><br/>
  <a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>

在这里插入图片描述

17.5 自定义异常

  1. 如果 Spring Boot 提供的异常不能满足开发需求,程序员也可以自定义异常.
  2. @ResponseStatus+自定义异常
  3. 底层是 ResponseStatusExceptionResolver ,底层调用 response.sendError(statusCode,resolvedReason);
  4. 当抛出自定义异常后,仍然会根据状态码,去匹配使用 x.html 显示

需求:自定义一个异常 AccessException, 当用户访问某个无权访问的路径时,抛出该异常,显示自定义异常状态码

  1. 创建 com/hspedu/usersys/exception/AccessException.java
package org.example.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/5 15:33
 * 自定义一个异常 可以继承Exception 或 RuntimeeeException
 */
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class AccessException extends RuntimeException{
    public AccessException() {
    }
    public AccessException(String message) {
        super(message);
    }

}

  1. 修改 MyErrorController.java,添加err3
    @GetMapping("/err3")
    public String err3(String name) {
        if(!"tom".equals(name)) {
            throw new AccessException();
        }
        // return "redirect:/manage.html";  // 可以拿到数据
        return "forward:/manage.html";  // 可以拿到数据
        // return "manage.html";   // 默认是请求转发(?) 拿不到数据
    }
  1. 完成测试, 浏览器 http://localhost:8080/err3
    只要输入的name不是tom,就会报错。输入的是name,就会重定向到manage.html。
    在这里插入图片描述

18 注入 Servlet、Filter、Listener

18.1.1 文 档 :
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-web-applications.embedded-container.servlets-filters-listeners

18.2 基本介绍

  1. 考虑到实际开发业务非常复杂和兼容,Spring-Boot 支持将 Servlet、Filter、Listener 注入Spring 容器, 成为 Spring bean
  2. 也就是说明 Spring-Boot 开放了和原生 WEB 组件(Servlet、Filter、Listener)的兼容

18.3 应用实例 1-使用注解方式注入

需求 : 演示通过注解方式注入 Servlet、Filter、Listener

  1. 创建 com/hspedu/usersys/servlet/Servlet_.java
package org.example.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;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/5 19:51
 */
/*
* @WebServlet 表示要注入该 Servlet
* urlPatterns = {"/servlet01", "/servlet02"} 表示映射的路径
* 注意注入的原生Servlet 不会被Springboot拦截器拦截。
* */
@WebServlet(urlPatterns = {"/servlet01", "/servlet02"})
public class Servlet_ extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello, servlet");
    }
}

  1. 修改 com/hspedu/usersys/MainAPP.java , 加入@ServletComponentScan
package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 20:23
 */
@SpringBootApplication()
// 要求扫描 org.example 包下的原生方式注入的servlet。
@ServletComponentScan(basePackages = {"org.example"})
public class MainApp {
    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);

    }
}

  1. 完成测试, 浏览器 http://localhost:8080/servlet01

在这里插入图片描述
4. 创建 com/hspedu/usersys/servlet/Filter_.java

package org.example.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/5 20:02
 */
/*
* {"/css/*", "/images/*"} 表示访问这些路径时,才会用过滤器进行过滤
* 1. @WebFilter 表示 Filter_ 是一个过滤器,并注入容器
* 2. urlPatterns = {"/css/*", "/images/*"} 表示当请求 /css/目录下的资源或images/目录下的资源时,需要经过该过滤器。(不过这里过滤器直接放行了)
* 3. 经过过滤器(这里是直接放行)后,拦截器是否拦截要根据拦截器的拦截规则确定
* */
@WebFilter(urlPatterns = {"/css/*", "/images/*"})
@Slf4j
public class Filter_  implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("=========Filter init() ==================");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("=========Filter doFilter() ==================");
        // 为了方便观察过滤器处理的资源,我们输出一个uri
        // 由于 ServletRequest类型的request没有getRequestURI(), 所以需要将其类型转为HttpServletRequest
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpServletRequest.getRequestURI();
        StringBuffer requestURL = httpServletRequest.getRequestURL();
        System.out.println("请求的资源的URI=" + requestURI);
        System.out.println("请求的资源的URL=" + requestURL);

        filterChain.doFilter(servletRequest, servletResponse);      // 过滤器直接放行。 doFilter 表示继续走后面
    }

    @Override
    public void destroy() {
        log.info("=========Filter destroy().==================");
    }
}

  1. 创建 static/css/t.css, 作为测试文件
    在这里插入图片描述
  2. 完成测试 , 注意观察后台, 浏览器 : http://localhost:8080/css/t.css
    在这里插入图片描述

注意: 过滤器配置的 urlPatterns 也会经过 Spring-Boot 拦截器(根据拦截器的规则)所以为了看到效果,请在拦截器配置放行 /css/**在这里插入图片描述

在 servlet 匹配全部是 /* , 在 Spring-Boot 是/**
在这里插入图片描述

访问:http://localhost:8080/css/t.css 流程分析:

首先浏览器输入http://localhost:8080/css/t.css,将请求达到tomcat。然后tomcat 接收到这个请求,首先经过过滤器。由于过滤器是直接放行,所以没啥过滤的操作。然后经过servlet,经过拦截器。此时可以配置拦截器直接放行,如果不放行,就按照拦截器的逻辑,必须先登录,登陆后保留用户信息的session,才能访问t.css。

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
7. 创建 com/hspedu/usersys/servlet/Listener_.java

package org.example.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/5 20:02
 */
/*
* {"/css/*", "/images/*"} 表示访问这些路径时,才会用过滤器进行过滤
* 1. @WebFilter 表示 Filter_ 是一个过滤器,并注入容器
* 2. urlPatterns = {"/css/*", "/images/*"} 表示当请求 /css/目录下的资源或images/目录下的资源时,需要经过该过滤器。(不过这里过滤器直接放行了)
* 3. 经过过滤器(这里是直接放行)后,拦截器是否拦截要根据拦截器的拦截规则确定
* */
@WebFilter(urlPatterns = {"/css/*", "/images/*"})
@Slf4j
public class Filter_  implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("=========Filter init() ==================");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("=========Filter doFilter() ==================");
        // 为了方便观察过滤器处理的资源,我们输出一个uri
        // 由于 ServletRequest类型的request没有getRequestURI(), 所以需要将其类型转为HttpServletRequest
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpServletRequest.getRequestURI();
        StringBuffer requestURL = httpServletRequest.getRequestURL();
        System.out.println("请求的资源的URI=" + requestURI);
        System.out.println("请求的资源的URL=" + requestURL);

        filterChain.doFilter(servletRequest, servletResponse);      // 过滤器直接放行。 doFilter 表示继续走后面
    }

    @Override
    public void destroy() {
        log.info("=========Filter destroy().==================");
    }
}

  1. 完成测试 , 启动项目,观察后台输出
    在这里插入图片描述

18.4 应用实例 2-使用 RegistrationBean 方式注入

演示使用 RegistrationBean 注入 Servlet、Filter、Listener

  1. 创建 com/hspedu/usersys/config/RegisterConfig_.java
package org.example.config;

import org.example.servlet.Filter_;
import org.example.servlet.Listener_;
import org.example.servlet.Servlet_;
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;

import java.util.Arrays;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/6 9:01
 *  @Configuration 表示当前的 RegisterConfig_ 是一个配置类
 */
@Configuration
public class RegisterConfig_ {
    // 以RegistrationBean方式注入Servlet
    @Bean
    public ServletRegistrationBean Servlet_() {
        Servlet_ servlet = new Servlet_();
        return new ServletRegistrationBean(servlet, "/servlet01", "/servlet02");
    }

    // 以RegistrationBean方式注入Filter
    @Bean
    public FilterRegistrationBean Filter_() {
        Filter_ filter = new Filter_();
        FilterRegistrationBean<Filter_> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/images/*", "/css/*"));
        return filterFilterRegistrationBean;
    }

    // 以RegistrationBean方式注入Listener
    @Bean
    public ServletListenerRegistrationBean Listener() {
        Listener_ listener = new Listener_();
        return new ServletListenerRegistrationBean(listener);
    }
}

  1. 去掉相关的注解,再次完成测试(全部注释掉
    在这里插入图片描述
    在这里插入图片描述

18.5 注意事项和细节说明

请求 Servlet 时,为什么不会到达拦截器

原因分析:
√ 注入的 Servlet 会存在 Spring 容器
√ DispatherServlet 也存在 Spring 容器
在这里插入图片描述
3. 大家回忆一下我们讲过的 Tomcat 在对 Servlet url 匹配的原则, 多个 servlet 都能处理到同一层路径, 精确优先原则/最长前缀匹配原则.
4. 在 SpringBoot 中, 去调用@Controller 目标方法 是按照 DispatherServlet 分发匹配的机
制,

19 内置 Tomcat 配置和切换

  1. SpringBoot 支持的 webServer: Tomcat, Jetty, or Undertow
    在这里插入图片描述
  2. SpringBoot 应用启动是 Web 应用时。web 场景包-导入 tomcat
  3. 支持对 Tomcat(也可以是 Jetty 、Undertow)的配置和切换

19.2 内置 Tomcat 的配置

19.2.1 通过 application.yml 完成配置

# 内置tomcat的配置(通过配置文件)
server:
    # 端口
    port: 9999
    tomcat:
      threads:
      # 最大工作线程数
      max: 10
      # 最小工作线程数
      min-spare: 5
    # tomcat 启动的线程数达到最大时,接受排队的请求个数,默认值为 100
    accept-count: 200
    # 最大连接数
    max-connections: 2000
    # 还有其它就不一一列举

19.2.2 通过类来配置 Tomcat

  1. 通过类来配置 Tomcat(说明: 配置文件可配置的更全.),
package org.example.config;

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/6 10:14
 */
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        // factory 就是 server
        factory.setPort(9999);
    }
}

19.3 切换 WebServer, 演示如何切换成 Undertow

  1. 修改 pom.xml , 排除 tomcat , 加入 Undertow 包的依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 引入 spring-boot-starter-web 排除 tomcat -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!-- 引入 undertow -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
  1. 老韩说明: 因为去掉了 tomcat 的依赖,所以项目有使用到 tomcat 相关类/接口,就会报错,注销/删除这部分代码即可 , 运行项目,完成测试
    在这里插入图片描述

20 数据库操作

20.1 JDBC+HikariDataSource

演示 Spring Boot 如何通过 jdbc+HikariDataSource 完成对 Mysql 操作
说明: HikariDataSource : 目前市面上非常优秀的数据源, 是 springboot2 默认数据源
在这里插入图片描述

  1. 创建测试数据库和表
-- 创建 furns_ssm
DROP DATABASE IF EXISTS spring_boot;
CREATE DATABASE spring_boot;
USE spring_boot; -- 创建家居表
CREATE TABLE furn(
	`id` INT(11) PRIMARY KEY AUTO_INCREMENT, ## id
	`name` VARCHAR(64) NOT NULL, ## 家居名
	`maker` VARCHAR(64) NOT NULL, ## 厂商
	`price` DECIMAL(11,2) NOT NULL, ## 价格
	`sales` INT(11) NOT NULL, ## 销量
	`stock` INT(11) NOT NULL, ## 库存
	`img_path` VARCHAR(256) NOT NULL ## 照片路径
);

-- 初始化家居数据
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`) VALUES (NULL , ' 北 欧 风 格 小 桌 子 ' , ' 熊 猫 家 居 ' , 180 , 666 , 7 ,
'assets/images/product-image/1.jpg');

INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , ' 简 约 风 格 小 椅 子 ' , ' 熊 猫 家 居 ' , 180 , 666 , 7 ,
'assets/images/product-image/2.jpg');

INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , ' 典 雅 风 格 小 台 灯 ' , ' 蚂 蚁 家 居 ' , 180 , 666 , 7 ,
'assets/images/product-image/3.jpg');

INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , ' 温 馨 风 格 盆 景 架 ' , ' 蚂 蚁 家 居 ' , 180 , 666 , 7 ,
'assets/images/product-image/4.jpg');

SELECT * FROM furn;
  1. 进 行 数 据 库 开 发 , 在 pom.xml 引 入 data-jdbc starter
        <!--1. 进行数据库开发,引入 data-jdbc starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

在这里插入图片描述
在这里插入图片描述
3. Spring Boot 不知道项目要操作 Mysql 还是 Oracle , 需要在 pom.xml 指定导入数据库驱动, 并指定对应版本.

        <!-- 1. 引入操作 mysql 的驱动
             2. 这个驱动版本要和你实际操作的 mysql 版本对应
             3. 我们 spring-boot mysql 区别仲裁版本是<mysql.version>8.0.26</mysql.version>
             4. 这个 mysql 驱动版本也可以在 pom.xml properties 指定
            <properties>
            <mysql.version>8.1.0</mysql.version>
            </properties>
        -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.1.0</version>
        </dependency>
  1. 在 application.yml 配置操作数据源的信息
spring:
  servlet:
    multipart:
      max-file-size: 5MB
      max-request-size: 50MB
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML
  web:
    resources:
      # 修改/指定静态资源的访问路径
      static-locations: [ classpath:/public/, classpath:/static/ ]
  # 数据源配置
  datasource:
    url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: liu1457154996
    driver-class-name: com.mysql.cj.jdbc.Driver
  1. 创建 04_springboot_usersys\src\main\java\com\hspedu\usersys\bean\Furn.java
public class Furn {
    private Integer id;
    private String name;
    private String maker;
    private BigDecimal price;
    private Integer sales;
    private Integer stock;
    private String imgPath = "assets/images/product-image/1.jpg";
    //这里增加无参构造器和有参构造器

20.1.3 应用实例-测试结果

● test 目录下的 com/hspedu/usersys/ApplicationTests.java , 完成测试

package org.example;
import org.example.bean.Furn;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.List;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/6 23:43
 */
@SpringBootTest
public class ApplicationTests {

    @Autowired
    JdbcTemplate jdbcTemplate;
    @Test
    public void contextLoads() {
        BeanPropertyRowMapper<Furn> furnBeanPropertyRowMapper = new BeanPropertyRowMapper<>(Furn.class);
        List<Furn> furns = jdbcTemplate.query("select * from furn", furnBeanPropertyRowMapper);
        for (Furn furn : furns) {
            System.out.println(furn);
        }

        // 打印 数据库类
        Class<? extends DataSource> aClass = jdbcTemplate.getDataSource().getClass();
        System.out.println(aClass);
    }
}

在这里插入图片描述

20.2 整合 Druid 到 Spring-Boot

使用手册: https://github.com/alibaba/druid
Druid: 性能优秀,Druid 提供性能卓越的连接池功能外【Java 基础】,还集成了 SQL 监控,黑名单拦截等功能,强大的监控特性,通过 Druid 提供的监控功能,可以清楚知道连接池和 SQL 的工作情况,所以根据项目需要,我们也要掌握 Druid 和 SpringBoot 整合

整合 Druid 到 Spring-Boot 方式
● 自定义方式
● 引入 starter 方式

20.2.3 Durid 基本使用

需求: 将 Spring-Boot 的数据源切换成 Druid

  1. 修改 pom.xml , 引入 druid 依赖
        <!-- 引入 druid 依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.22</version>
        </dependency>
  1. 创建 com/hspedu/usersys/config/DruidDataSourceConfig.java 配置类

    @ConfigurationProperties("spring.datasource")对应:在这里插入图片描述

package org.example.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import javax.xml.crypto.Data;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/7 0:11
 * 配置类,用于配置Druid连接池
 */
@Configuration
public class DruidDataSourceConfig {
    // 默 认 的 自 动 配 置 是 判 断 容 器 中 没 有 才 会 配
    /**
     * @ConditionalOnMissingBean(DataSource.class)
     * 1. 默认的数据源配置是 @ConditionalOnMissingBean(DataSource.class)
     * 2. 也就是当容器中没有 DataSource 组件时,才会注入,如果我们这里配置了
     DataSource, 就会使用我们配置的数据源
     * 3. "spring.datasource" 会将 druid 数据源的配置绑定到 application.yml, 就不需要
     setXxx
     */
    @ConfigurationProperties("spring.datasource")
    @Bean
    public DataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        // "spring.datasource" 会将 druid 数据源的配置绑定到 application.yml, 就不需要setXxx
        // druidDataSource.setUrl();
        // druidDataSource.setUsername();
        // druidDataSource.setPassword();
        return druidDataSource;
    }
}

  1. 完成测试,运行 ApplicationTests.java , 观察数据源的运行类型
    在这里插入图片描述

20.2.4 Durid 监控功能-SQL 监控

需求: 配置 Druid 的监控功能,包括 SQL 监控、SQL 防火墙、Web 应用、Session 监控等
在这里插入图片描述

20.2.4.2 SQL 监控数据
  1. 修改 com/hspedu/usersys/config/DruidDataSourceConfig.java , 增加 druid 监控功能 参考地 址 :
    在这里插入图片描述
package org.example.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.sql.SQLException;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/7 0:11
 * 配置类,用于配置Druid连接池
 */
@Configuration
public class DruidDataSourceConfig {
    // 默 认 的 自 动 配 置 是 判 断 容 器 中 没 有 才 会 配
    /**
     * @ConditionalOnMissingBean(DataSource.class)
     * 1. 默认的数据源配置是 @ConditionalOnMissingBean(DataSource.class)
     * 2. 也就是当容器中没有 DataSource 组件时,才会注入,如果我们这里配置了
     DataSource, 就会使用我们配置的数据源
     * 3. "spring.datasource" 会将 druid 数据源的配置绑定到 application.yml, 就不需要
     setXxx
     */
    @ConfigurationProperties("spring.datasource")
    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        // "spring.datasource" 会将 druid 数据源的配置绑定到 application.yml, 就不需要setXxx
        // druidDataSource.setUrl();
        // druidDataSource.setUsername();
        // druidDataSource.setPassword();

        // 加入监控功能
        druidDataSource.setFilters("stat");
        return druidDataSource;
    }

    /*
    * 配置druid的监控页功能
    * 这里是要配置一个StatViewServlet, 是一个标准的HttpServlet,所以这里使用ServletRegistrationBean() 方式注入Servlet Bean
    * 除此之外,还可以用@WebServlet注解方式,继承HttpServlet 方式注入Servlet Bean
    * */
    @Bean
    public ServletRegistrationBean statViewServlet() {
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");	   // "/druid/*" 是映射路径。
        //配置登录监控页面用户名和密码
        statViewServletServletRegistrationBean.addInitParameter("loginUsername", "Lgq");
        statViewServletServletRegistrationBean.addInitParameter("loginPassword", "123456");

        return statViewServletServletRegistrationBean;
    }
}


  1. 完成测试: 访问 http://localhost:10000/druid/index.html(实际上访问druid/*都可以进入该页面) 不会被拦截 , 如果没有问题,小伙伴会看到这个页面
    在这里插入图片描述
  2. 修改 com/hspedu/usersys/config/DruidDataSourceConfig.java , 加入监控功能。参考: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter
    在这里插入图片描述
    在这里插入图片描述
  3. 创建 com/hspedu/usersys/controller/DruidSqlController.java ,模拟操作 DB 的请求
package org.example.controller;

import org.example.bean.Furn;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.concurrent.LinkedTransferQueue;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/9 22:20
 * 模拟操作DB请求
 */
@Controller
public class DruidSqlController {
    @Autowired
    private JdbcTemplate jdbcTemplate;


    @ResponseBody   // 以json格式数据返回
    @GetMapping("/sql")
    public List<Furn> crudDB() {
        BeanPropertyRowMapper<Furn> furnBeanPropertyRowMapper = new BeanPropertyRowMapper<>(Furn.class);
        List<Furn> furns = jdbcTemplate.query("select * from furn", furnBeanPropertyRowMapper);
        for (Furn furn : furns) {
            System.out.println(furn);
        }
        return furns;
    }
}

● 完成测试, 观察 SQL 监控数据, 浏览器 http://localhost:10000/druid/sql.html
此时可以监控到sql的访问:
在这里插入图片描述
不过访问得到的数据不美观,并不是@ResponseBody返回的JSON数据:
在这里插入图片描述
此时需要配置浏览器的内容协商:在application.yml文件中配置内容协商:
在这里插入图片描述

spring:
   mvc:
    hiddenmethod:
      filter:
        enabled: true # 开启页面表单的Rest功能。
    contentnegotiation:
      favor-parameter: true # 开启基于请求参数的内容协商功能

访问: http://localhost:8080/sql?format=json 即可得到JSON格式在这里插入图片描述

20.2.5 Durid 监控功能-Web 关联监控

需求: 配置 Web 关联监控配置:Web 应用、URI 监控
官方文档 https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
在这里插入图片描述

  1. 修 改 com/hspedu/usersys/config/DruidDataSourceConfig.java , 注 入 / 增 加 WebStatFilter 用于采集 web-jdbc 关联监控的数据
 /*
    * 注入 WebStatFilter 用于采集 web-jdbc 关联监控的数据
    * 配置druid的web关联监控配置
    * */
    @Bean
    public FilterRegistrationBean webStatFilter() {
        WebStatFilter webStatFilter = new WebStatFilter();
//        StatViewFilter statViewFilter = new StatViewFilter();
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
        // 配置 默认对所有的URL请求监控
        // setUrlPatterns 的参数是Collection, 这里用Arrats.asList() 转成集合。
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
        // 排除URL
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
  1. 为了测试方便,修改 com/hspedu/usersys/config/WebConfig.java, 放行 /sql 请求, 不再对sql的访问使用拦截器拦截。
/**
 * @Author: GQLiu
 * @DATE: 2024/5/2 23:57
 * <p>
 * 注册拦截器
 * <p>
 * 这里实现 Spring-Boot 定制功能, 加入自己的配置
 */
// lite 模式下,直接返回新实例对象
@Configuration
public class WebConfig  implements WebMvcConfigurer  {

    // 方式 一:
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")     // 拦截所有请求
                .excludePathPatterns("/", "/login", "images/**", "upload.html", "/upload", "/css/**", "/sql");  // 放行的请求
    }

● 完成测试,重启项目,看看 Web 应用和 URI 监控是否生效
在这里插入图片描述
在这里插入图片描述

20.2.6 Durid 监控功能-SQL 防火墙

官方文档 https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
在这里插入图片描述
在这里插入图片描述
配置方式十分简单:

  1. 修改 com/hspedu/usersys/config/DruidDataSourceConfig.java ,加入防火墙监控
    在这里插入图片描述
    查看Sql防火墙拦截效果:在这里插入图片描述
20.2.7 Durid 监控功能-Session 监控

官方文档 https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
在这里插入图片描述
Session监控监控的是哪个会话,哪个系统呢?
监控的不是druid监控页面这个会话,而是家具用户管理系统页面的会话。
在这里插入图片描述
在这里插入图片描述

20.2.8 Druid Spring Boot Starter

前面使用的是自己引入druid+配置类方式整合Druid和监控
Druid Spring Boot Starter 可以让程序⚪在Spring Boot项目中更加轻松集成Druid和监控

需求: 使用 Druid Spring Boot Starter 方式完成 Druid 集成和监控

  1. 修改 pom.xml 注销 druid 的依赖
    在这里插入图片描述

  2. 注销 com/hspedu/usersys/config/DruidDataSourceConfig.java
    在这里插入图片描述

  3. 这时测试,druid 失效
    在这里插入图片描述

  4. 查看 druid 文档 https://github.com/alibaba/druid,引入 druid starter
    在这里插入图片描述

  5. 导入 druid-springb-boot-starter依赖
    在这里插入图片描述
    查看其导入了哪些依赖
    在这里插入图片描述

  6. 修改 resources/application.yml 增加配置参数

spring:
  servlet:
    multipart:
      max-file-size: 5MB
      max-request-size: 50MB
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML
  mvc:
    hiddenmethod:
      filter:
        enabled: true # 开启页面表单的Rest功能。
    contentnegotiation:
      favor-parameter: true # 开启基于请求参数的内容协商功能
  web:
    resources:
      # 修改/指定静态资源的访问路径
      static-locations: [ classpath:/public/, classpath:/static/ ]
  # 数据源配置
  datasource:
    url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    # druid配置
    druid:
#      filters: stat, wall, slf4j    # 这一行决定哪些活动要开启
      # 配置druid以及druid的监控页
      stat-view-servlet:
        enabled: true
        login-username: lgq
        login-password: 123456
        reset-enable: false
      # 配置 web 监控, 用于采集web-jdbc关联的监控数据 是对应查看URI监控
      web-stat-filter:
        enabled: true
        url-pattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'

      # 启用filter
      filter:
        # 配置sql 监控
        stat:
          slow-sql-millis: 1000   # 慢查询
          log-slow-sql: true      # 启用慢查询的结果日志
          enabled: true
        # 防火墙
        wall:
          enabled: true
#          config:
#            drop-table-allow: false   # 不允许删除表的操作
#            select-all-column-allow: false  # 不允许select * from xxx

重启项目,完成测试
访问 http://localhost:8080/sql
在这里插入图片描述
在这里插入图片描述

21 Spring Boot 整合 MyBatis

需求:查询出一条数据
在这里插入图片描述

  1. 创建数据库和表
CREATE DATABASE `springboot_mybatis`
DROP TABLE `monster`
use `springboot_mybatis`
CREATE TABLE `Monster` (
	`id` INT NOT NULL AUTO_INCREMENT,
	`age` INT NOT NULL,
	`birthday` DATE DEFAULT NULL,
	`email` VARCHAR(255) DEFAULT NULL,
	`gender` char(1) DEFAULT NULL,
	`name` VARCHAR(255) DEFAULT NULL,
	`salary` DOUBLE NOT NULL,
	 PRIMARY KEY (`id`)
) CHARSET=utf8

insert into Monster values(null, 20, '2000-11-11', 'nmw@sohu.com', '男', '牛魔王', 5000.88);
insert into Monster values(null, 10, '2011-11-11', 'bgj@sohu.com', '女', '白骨精', 8000.88);

SELECT * FROM `Monster`
  1. 创建 05_springboot_mybatis 项目-使用灵活的方式创建 maven
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lgq</groupId>
    <artifactId>springboot_mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--导入SpringBoot父工程,规定的写法-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
        <!--
    在build种配置resources, 防止我们资源导出失败的问题.
    1. 不同idea/maven版本可能提示错误不一样.
    2. 以不变应万变, 少什么文件, 就增加相应的配置即可.
    3. 含义是将 src/main/java目录和子目录以及 src/main/resources目录和子目录下的资源文件xml和properties在build项目时,导出到对应target目录下

    -->

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>
    </build>
    

    <!--引入相关的依赖-->
    <dependencies>
        <!--引入web starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.5.3</version>
        </dependency>

        <!--引入mybatis starer-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!--引入mysql驱动-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.1.0</version>
        </dependency>

        <!--引入配置处理器
            用于去配置application.yml
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--引入lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <optional>true</optional>
        </dependency>

        <!--引入springboot test
        为了在测试中能够使用 @SpringBootTest 注解
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--引入druid依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>

        <!--导入mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
    </dependencies>

</project>
  1. 创建 resources/application.yml , 配置数据源参数, 并完成 Spring Boot 项目启动测试
    在这里插入图片描述
server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_mybatisplus?useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: liu1457154996  # 实验室密码123456

# 指定mybatis的配置
mybatis:
  # 指定 XXXmapper.xml 文件的配置
  mapper-locations: classpath:mapper/*.xml
  # 配置原来的类型别名
  #  type-aliases-package: com.lgq.springboot.mybatis.bean
  config-location: classpath:mybatis-config.xml

  # 什么时候写单独的mybatis-config.xml, 什么时候直接在application.yml中
  # 配置文件很多时,单独写一个mybatis-config.xml. 配置文件不多,直接在application.yml文件中写

mybatis-plus:
  type-aliases-package: com.lgq.springboot.mybatis.bean

logging:
  level:
    org.mybatis: DEBUG

  1. 切换数据源为 druid , 修改 pom.xml(如果没有 mybatis-stater , 加入即可.) , 并加入配置文件 com/hspedu/mybatis/config/DruidDataSourceConfig.java , 完成测试

在pom.xml文件中:

        <!--引入druid依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>
package com.lgq.springboot.mybatis.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewFilter;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;

/**
 * @author GQLiu
 * @date 2024/5/12 15:00
 * 配置类, 用于配置Druid连接池
 */
@Configuration
public class DruidDataSourceConfig {
    // 默 认 的 自 动 配 置 是 判 断 容 器 中 没 有 才 会 配
    /**
     * @ConditionalOnMissingBean(DataSource.class)
     * 1. 默认的数据源配置是 @ConditionalOnMissingBean(DataSource.class)
     * 2. 也就是当容器中没有 DataSource 组件时,才会注入,如果我们这里配置了
     DataSource, 就会使用我们配置的数据源
     * 3. "spring.datasource" 会将 druid 数据源的配置绑定到 application.yml, 就不需要
     setXxx
     */
    @ConfigurationProperties("spring.datasource")
    @Bean
    public DataSource dataSource() throws SQLException, SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        // "spring.datasource" 会将 druid 数据源的配置绑定到 application.yml, 就不需要setXxx
        // druidDataSource.setUrl();
        // druidDataSource.setUsername();
        // druidDataSource.setPassword();

        // 加入监控功能
        // 添加 ,wall 表示添加sql防火墙功能
        // druidDataSource.setFilters("stat, wall");
        return druidDataSource;
    }

    /*
    * 配置druid的监控页功能
    * 这里是要配置一个StatViewServlet, 是一个标准的HttpServlet,所以这里使用ServletRegistrationBean() 方式注入Servlet Bean
    * 除此之外,还可以用@WebServlet注解方式,继承HttpServlet 方式注入Servlet Bean
    * */
    @Bean
    public ServletRegistrationBean statViewServlet() {
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
        //配置登录监控页面用户名和密码
        statViewServletServletRegistrationBean.addInitParameter("loginUsername", "Lgq");
        statViewServletServletRegistrationBean.addInitParameter("loginPassword", "123456");

        return statViewServletServletRegistrationBean;
    }

    /*
    * 注入 WebStatFilter 用于采集 web-jdbc 关联监控的数据
    * 配置druid的web关联监控配置
    * */
    @Bean
    public FilterRegistrationBean webStatFilter() {
        WebStatFilter webStatFilter = new WebStatFilter();
//        StatViewFilter statViewFilter = new StatViewFilter();
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
        // 配置 默认对所有的URL请求监控
        // setUrlPatterns 的参数是Collection, 这里用Arrats.asList() 转成集合。
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
        // 排除URL
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

测试当前数据源

package com.lgq.springboot.mybatis;

import com.lgq.springboot.mybatis.bean.Monster;
import com.lgq.springboot.mybatis.mapper.MonsterMapper;
import com.lgq.springboot.mybatis.service.MonsterService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

@SpringBootTest
public class ApplicationTest {

    @Resource
    JdbcTemplate jdbcTemplate;

    @Test
    public void t1() {
        // 输出看看当前的数据源是什么
        System.out.println(jdbcTemplate.getDataSource().getClass());
    }




}

在这里插入图片描述

  1. 创建com/hspedu/mybatis/bean/Monster.java
package com.lgq.springboot.mybatis.bean;


import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

/**
 * @author GQLiu
 * @date 2024/5/12 15:56
 */
@Data
public class Monster {
    private Integer id;
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date birthday;
    private String email;
    private String name;
    private String gender;
    private Double salary;

    public Monster() {
    }
}
  1. 创建 com/hspedu/mybatis/mapper/MonsterMapper.java
package com.lgq.springboot.mybatis.mapper;
import com.lgq.springboot.mybatis.bean.Monster;
import org.apache.ibatis.annotations.Mapper;


/**
 * @author GQLiu
 * @date 2024/5/12 16:10
 */
@Mapper
public interface MonsterMapper {
    // 根据 id 得到Monster
    public Monster getMonsterById(Integer id);
}

  1. 创建 05_springboot_mybatis\src\main\resources\mapper\MonsterMapper.xml , 文件模板从 mybatis 官方文档拷贝
<?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.lgq.springboot.mybatis.mapper.MonsterMapper">
    <!--在application.yml文件中配置了简写。 这里的resultType可以直接写Monster-->
    <select id="getMonsterById" resultType="Monster">
        SELECT * FROM `Monster` WHERE id = #{id}
    </select>
</mapper>
  1. 创 建 com/hspedu/mybatis/service/MonsterService.java 和com/hspedu/mybatis/service/impl/MonsterServiceImpl.java
    在这里插入图片描述
package com.lgq.springboot.mybatis.service;

import com.lgq.springboot.mybatis.bean.Monster;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/16 22:33
 */
public interface MonsterService{
    public Monster getMonsterById(Integer id);

}

package com.lgq.springboot.mybatis.service.impl;

import com.lgq.springboot.mybatis.bean.Monster;
import com.lgq.springboot.mybatis.mapper.MonsterMapper;
import com.lgq.springboot.mybatis.service.MonsterService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/16 22:34
 */
@Service    // 一定注意 这个 Service 注解要写在实现类上,而不是写在接口上,不然会报错!!
public class MonsterServiceImpl implements MonsterService {

    @Resource
    private MonsterMapper monsterMapper;


    @Override
    public Monster getMonsterById(Integer id) {
        return monsterMapper.getMonsterById(id);
    }
}

测试MonsterMapper

package com.lgq.springboot.mybatis;

import com.lgq.springboot.mybatis.bean.Monster;
import com.lgq.springboot.mybatis.mapper.MonsterMapper;
import com.lgq.springboot.mybatis.service.MonsterService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

@SpringBootTest
public class ApplicationTest {

    @Resource
    JdbcTemplate jdbcTemplate;

    @Resource
    private MonsterMapper monsterMapper;
    @Test
    public void t1() {
        // 输出看看当前的数据源是什么
        System.out.println(jdbcTemplate.getDataSource().getClass());
    }

    // 测试MonsterMapper接口
    @Test
    public void getMonsterById() {
        Monster monster = monsterMapper.getMonsterById(1);
        System.out.println(monster);
    }


}

在这里插入图片描述
9. 创建 com/hspedu/mybatis/controller/MonsterController.java(SpringMVC那一套)

package com.lgq.springboot.mybatis.controller;

import com.lgq.springboot.mybatis.bean.Monster;
import com.lgq.springboot.mybatis.service.MonsterService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/19 10:13
 */
@Controller
public class MonsterController {
    @Resource
    private MonsterService monsterService;

    @ResponseBody
    @GetMapping("/monster")
    public Monster getMonsterById(@RequestParam("id") Integer id) {
        return monsterService.getMonsterById(id);
    }
}

  1. 修改 resources/application.yml , 指定 mybatis 的配置参数

# 指定mybatis的配置
mybatis:
  # 指定 XXXmapper.xml 文件的配置
  mapper-locations: classpath:mapper/*.xml
  # 配置原来的类型别名
  #  type-aliases-package: com.lgq.springboot.mybatis.bean
  config-location: classpath:mybatis-config.xml

  # 什么时候写单独的mybatis-config.xml, 什么时候直接在application.yml中
  # 配置文件很多时,单独写一个mybatis-config.xml. 配置文件不多,直接在application.yml文件中写

在这里插入图片描述

21.3 注意事项和细节说明

  1. spring boot 整合 mybatis 取出的日期, 出现 8 小时时差 解决方案
    在这里插入图片描述
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")

22 Spring Boot 整合 MyBatis-Plus

MyBatis-Plus 官网 https://baomidou.com
在这里插入图片描述

22.2 基本介绍

  1. MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
  2. 强大的 CRUD 操作:内置通用 Mapper、通用 Service,通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

22.3 整合 MyBatis-Plus 实例

需求:查询
在这里插入图片描述

  1. 创建数据库和表
CREATE DATABASE `springboot_mybatisplus` 
USE `springboot_mybatisplus` 
CREATE TABLE `monster` (
		`id` INT NOT NULL AUTO_INCREMENT,
		`age` INT NOT NULL, 
		`birthday` DATE DEFAULT NULL, 
		`email` VARCHAR(255) DEFAULT NULL, 
		`gender` CHAR(1) DEFAULT NULL, 
		`name` VARCHAR(255) DEFAULT NULL, 
		`salary` DOUBLE NOT NULL,
		 PRIMARY KEY (`id`)
) CHARSET=utf8

SELECT * FROM `monster`

INSERT INTO monster VALUES(NULL, 20, '2000-11-11', 'xzj@sohu.com', '男', ' 蝎 子 精 ',
15000.88);

INSERT INTO monster VALUES(NULL, 10, '2011-11-11', 'ytj@sohu.com', '女', ' 玉 兔 精 ',
18000.88);
  1. 创建 06_springboot_mybatisplus 项目
    -pom.xml 引入必要的依赖
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>springboot_mybatisplus</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>springboot_mybatisplus</name>
  <url>http://maven.apache.org</url>



  <!--导入SpringBoot父工程,规定的写法-->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.3</version>
  </parent>
  <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
  <!--
在build种配置resources, 防止我们资源导出失败的问题.
1. 不同idea/maven版本可能提示错误不一样.
2. 以不变应万变, 少什么文件, 就增加相应的配置即可.
3. 含义是将 src/main/java目录和子目录以及 src/main/resources目录和子目录下的资源文件xml和properties在build项目时,导出到对应target目录下

-->

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.yml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
    </resources>
  </build>
  
  <!--引入相关的依赖-->
  <dependencies>


    <!--引入web starter-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>2.5.3</version>
    </dependency>



    <!--引入mysql驱动-->
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <version>8.1.0</version>
    </dependency>

    <!--引入配置处理器
        用于去配置application.yml
    -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>

    <!--引入lombok-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.20</version>
      <optional>true</optional>
    </dependency>

    <!--引入springboot test
    为了在测试中能够使用 @SpringBootTest 注解
    -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <!--引入druid依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.17</version>
    </dependency>

    <!--导入mybatis-plus starter -->
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.4.3</version>
    </dependency>
  </dependencies>
</project>

  1. 创建 resources/application.yml 配置数据源参数
server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_mybatisplus?useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: liu1457154996  # 实验室密码123456


# 指定 mybatis plus配置
mybatis-plus:
#  type-aliases-package: com.lgq.springboot.mybatis.bean
#
#  # 指定 XXXmapper.xml 文件的配置
#  mapper-locations: classpath:mapper/*.xml
#  # 配置原来的类型别名
  type-aliases-package: com.lgq.springboot.mybatis.bean
#  config-location: classpath:mybatis-config.xml
  # 指定配置,输出日志
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 什么时候写单独的mybatis-config.xml, 什么时候直接在application.yml中
  # 配置文件很多时,单独写一个mybatis-config.xml. 配置文件不多,直接在application.yml文件中写

logging:
  level:
    org.mybatis: DEBUG

  1. 切 换 数 据 源 为 druid , 修 改 pom.xml 和 创 建 配 置 文 件com/hspedu/mybatisplus/config/DruidDataSourceConfig.java
    同mybatis

  2. 测试是否能正确启动项目, 注意观察 mybatis-plus 是否引入成功
    在这里插入图片描述

  3. 创建 com/hspedu/mybatisplus/bean/Monster.java

package org.example.mybatisplus.bean;


import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

/**
 * @author GQLiu
 * @date 2024/5/12 15:56
 * 如果实体类Monster 和 表名 monster 是对应的,可以直接映射
 * 如果不一致,需要用 @TableName(value = "表名") 来进行映射。
 */
@Data
// @TableName(value = "monster_")
public class Monster {
    private Integer id;
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date birthday;
    private String email;
    private String name;
    private String gender;
    private Double salary;

    public Monster() {
    }
}
  1. 创建 com/hspedu/mybatisplus/mapper/MonsterMapper.java
package org.example.mybatisplus.mapper;
import org.apache.ibatis.annotations.Param;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.mybatisplus.bean.Monster;


/**
 * @author GQLiu
 * @date 2024/5/12 16:10
 * 1. BaseMapper 已经默认定义了很多的CRUD方法, 可以直接使用
 * 2. 如果BaseMapper 提供的方法不满足业务需求,可以在XXXMapper.java中声明方法,并在XXXMapper.xml文件配置。
 */
@Mapper
public interface MonsterMapper extends BaseMapper<Monster> {
    // 添加一个insert方法

    int insertSelective(Monster monster);

    int delById(@Param("id") Integer id);
}

  1. 创建 com/hspedu/mybatisplus/service/MonsterService.java
package org.example.mybatisplus.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.mybatisplus.bean.Monster;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/16 22:33
 * 1. 传统方式:在接口中,定义方法/声明方法,然后在实现类中进行实现
 * 2. 在mybatis-plus中,接口 可以继承 父接口 IService
 * 3. 这个IService接口声明了很多方法, 比如crud
 * 4. 默认提供的方法不能满足要求,可以再声明需要的方法,然后在实现类中实现
 */
public interface MonsterService extends IService<Monster> {
    // 自定义方法

}

  1. 创建 com/hspedu/mybatisplus/service/impl/MonsterServiceImpl.java
package org.example.mybatisplus.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.mybatisplus.bean.Monster;
import org.example.mybatisplus.mapper.MonsterMapper;
import org.example.mybatisplus.service.MonsterService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/16 22:34
 * 1. 传统方式:在实现类Impl 直接实现 MonsterService
 * 2. 在mybatis-plus中,我们开发 Service 实现类,需要继承 ServiceImpl
 * 3. 我们观察到ServiceImpl类实现了IService接口
 * 4. MonsterService 接口继承了IService接口
 * 5. 所以这里的MonsterServiceImpl 可以认为是实现了 MonsterService 接口 。
 *    这样MonsterServiceImpl 就可以实现 IService方法
 * 6. 总结: 实现MonsterService是为了在Impl中实现自定义的方法
 *         继承ServiceImpl
 */
@Service    // 一定注意 这个 Service 注解要写在实现类上,而不是写在接口上,不然会报错!!
public class MonsterServiceImpl extends ServiceImpl<MonsterMapper, Monster>
        implements MonsterService {
    @Resource
    private MonsterMapper monsterMapper;
}

  1. 创建 com/hspedu/mybatisplus/controller/MonsterController.java
package org.example.mybatisplus.controller;


import org.example.mybatisplus.bean.Monster;
import org.example.mybatisplus.service.MonsterService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/19 10:13
 */
@Controller
public class MonsterController {
    @Resource
    private MonsterService monsterService;

    @ResponseBody
    @GetMapping("/monster")
    public Monster getMonsterById(@RequestParam("id") Integer id) {
        return monsterService.getById(id);
    }

    @ResponseBody
    @GetMapping("/list")
    public List<Monster> listMonster() {
        return monsterService.list();
    }
}

  1. 修改 com/hspedu/mybatisplus/Application.java , 加入对 Mapper 的扫描
    如果不想每个XxxMapper.java都去写@Mapper注释,可以通过在SpringBoot主程序中编写 @MapperScan 注解,加入对Mapper的扫描
    在这里插入图片描述
package org.example.mybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.swing.*;

/**
 * @Author: GQLiu
 * @DATE: 2024/5/19 11:06
 * @MapperScan 可以指定扫描某个包下的所有都当作mapper。不用在每个XXXMapper类中写@Mapper
 */
@MapperScan(basePackages = {"org.example.mybatisplus.mapper"})
@SpringBootApplication
public class MainApp {
    public static void main(String[] args) {
        SpringApplication.run(MainApp.class, args);
    }
}

在这里插入图片描述
在这里插入图片描述

22.4 整合 MyBatis-Plus 注意事项和细节

  1. @TableName 作用
@TableName(value = "表名") 来进行映射。

在这里插入图片描述
2. MyBatis-Plus starter 到底引入了哪些依赖?
在这里插入图片描述
3. 为 了 开 发 方 便 , 可 以 安 装 MyBatisX 插 件 , 参 考 文 档 :https://baomidou.com/guide/mybatisx-idea-plugin.html
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到