目录
7.检查 JDK 没有正确在IDEA配置的问题(没有的则跳过)
9.编写测试 SpringBoot 内嵌 Tomcat 服务
1.4在springboot数据库中创建一张用户表,用于CRUD操作
4. Spring Boot 整合 JPA 操作 MySQL
4.3 编写接口继承实现接口 JpaRepository,自定义查询语句,>
4.4 编写测试类,对JPA 接口自带CRUD方法操作数据表进行测试
4.5.1 编写测试类,进行JPA自定义SQL语句操作MySQL
四、Redis 缓存(铺垫) | 编写接口 | 视图技术(Thymeleaf)
2.2 在全局配置文件 application.propertis 中配置
3. 运行 Spring Boot 并测试(学习编写接口和视图技术)
4. 编写 service 服务层控制 UserRepository 接口 (缓存逻辑)
6.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解先注释掉 :
6.2 运行我们的 Spring Boot 项目,然后打开我们的浏览器进行访问接口
7.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解的注释进行还原 :
2.3 打包报<编码UTF-8的不可映射字符>解决 (如没有可跳过)
3.2 主程序入口继承 SpringBootServletInitializer 类
资源
项目地址
码云:Spring Boot Demo
PS
Spring Boot 的主要优点包括:
- 简化配置。它采用约定优于配置的原则,提供了自动配置功能,可以减少开发人员的大量手动配置工作。
- 快速开发。它提供了快速开发的工具和功能,如热部署、自动重载、自动刷新,以及内嵌的Web服务器,如Tomcat、Jetty和Undertow,简化了部署过程。
- 微服务支持。它支持微服务架构,提供了一系列功能和工具,如服务发现、负载均衡、熔断器等,帮助构建和管理微服务应用程序。
- 为SpringCloud微服务奠定基础,使微服务构建变得简单
一、新建 SpringBoot 项目
- 方式一:使用 Maven 创建 SpringBoot 项目
- 方式二:使用 Spring InitiaIizr 创建SpringBoot 项目
第二种方式更为简便,所以这里直接用第二种方式 :
- JDK 19
- IDEA 2021
- Spring Boot 3.2.3
Spring Boot 支持的 JDK 版本简述:
Spring Boot 版本 2.4.x 及之前的版本都支持 JDK 8。这意味着,如果你使用 JDK 8,你可以选择 Spring Boot 2.4.x 或之前的版本来构建应用程序。不过,从 Spring Boot 2.5.0 开始,官方不再支持 JDK 8,最低要求提升到 JDK 11。
1.我这里连接了码云仓库
2.新建项目
2.1不用码云的的创建方式
PS:File ——> New ——> Project
2.2使用码云的创建方式
PS:项目右键 ——> New ——> Module
3.使用 Spring InitiaIizr 创建项目
- Server URL:start.spring.io ,默认
- Name:项目名
- Location:项目本地保存路径
- Language :使用的编程语言
- Type:使用的依赖仓库管理类型,选 Maven
- Group:组 id,会配置到 pom.xml
- Artifact:定位名,会配置到 pom.xml
- Package name:包名,跟着源码包名一起创建。
- Project SDK:JDK 版本。
- Java:使用的具体编译 Java 版本,会配置到 pom.xml。
4.选择基本 Dependencies 依赖项
5.设置项目与文件编码格式 UTF-8
PS:File ——> Settings ——> Editor ——> File Encodings 。
Project Encoding 选项选 GBK,其它都是 UTF-8 编码。
控制台也设置一下,要不然中文可能乱码:
PS:File ——> Settings ——> Editor ——> General ——> Console 。
Default Encoding 选项选择 UTF-8 编码。
6.观察我们的项目架构
7.检查 JDK 没有正确在IDEA配置的问题(没有的则跳过)
8.测试运行时报异常(这是因为我们用了MyBatis依赖)
8.1解决方式一(配置连接MySQL)
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
请更改为你定义的数据库名(YouMySQLDataName)和数据库用户名和密码
spring.datasource.url=jdbc:mysql://localhost:3306/YouMySQLDataName
spring.datasource.username=root
spring.datasource.password=123456
8.2解决方式二(忽略扫描装载和数据库有关的东西)
在注解@SpringBootApplication的程序入口类,禁止
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
9.编写测试 SpringBoot 内嵌 Tomcat 服务
右击DemoApplication启动运行,等待运行成功后,在浏览器地址栏输入
http://localhost:8080/hello
出现错误,发现地址重定向到 /login :
9.1 解决重定向到 /login 问题
原因分析:
- 依赖中加入了spring security,SpringSecurity的依赖
- SecurityAutoConfiguration 是Spring Boot提供的安全自动配置类(也就是说它自动集成了SpringSecurity)
解决:在注解@SpringBootApplication的程序入口类,加入禁止自动配置
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
因为我们添加了数据库依赖,根据前面忽略数据库的也可以先加入,后面CRUD需要删除忽略数据库的加载类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class})
9.2 低级 404 问题
再次运行报 404 问题:
原因分析:
- SpringBoot 默认只会扫描自动装载与 @SpringBootApplication 注解启动入口同包下的所有文件或所有子文件。如启动类在com包下,则会扫描当前com包下所有文件和子文件。
- 自动装配的注解包括
@Controller
、@RestController、@Service
、@Component
、@Repository
等等。 - 我这里的启动类和
@RestController注解类
并不是在同一个包下。
解决404问题:
- 可以把启动类放到 com 包下。
- 使用 @ComponentScan 注解指定扫描包、文件、自定义类扫描器。
1. 推荐采用第一种方式,直接将启动类拖到com包下
2.第二种方式,指定扫描包,而不是把启动类放到com包下扫描全部,在大型项目中多了就慢。
@ComponentScan(value = "com")
注解 @ComponentScan 是可以定义多个的,我这里定义多余了,这里只要随便一个就行
9.3 服务运行成功
二、SpringBoot 配置&注解
1. 配置文件
全局配置文件: src/main/resources/
-
Spring Initializr 自动生成:application.properties
-
语法:(使用点分制) 主配置名.子配置名.子配置名=配置项或值
-
-
手动创建全局配置文件: application.yaml 或 .yml 文件格式
-
主配置名:(冒号)回车 子配置名:(冒号)空格 配置项或值
-
1.1MySQL 配置
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456
属性对象 | 值:解释 |
spring.datasource.url | jdbc:mysql://localhost:3306/springboot 是一种url协议的一种,使用jdbc连接mysql,3306则是端口, springboot 是连接的数据库名 通常数据库名后,可以加get请求的参数如:jdbc:mysql://localhost:3306/springboot?encoding=utf-8 没有特殊需求默认就好,因为编码默认就是utf-8 |
spring.datasource.username | 数据库用户名 |
spring.datasource.password | 数据库密码 |
2. pom.xml
PS:用于配置我们要使用的依赖库,一般如:
<dependencies> <!-- Spring Data Redis依赖启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Spring DAta JPA 依赖启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency></dependencies>
依赖标签 | 解释 |
dependencies | 编写依赖的内容 |
dependency | 每个依赖的个体 |
groupId | 此依赖所属组ID |
artifactId | 依赖ID |
说了跟说一样
三、MyBatis CRUD MySQL
0. PS
这里展示最简单的三种方式
- 注解的方式,配合SQL语句。
- XML配置文件的方式,在配置文件中编写SQL,配置文件的方式必须得掌握,在开发中可以很好的分离操作SQL语句。
- JPA框架,是第一种方式的延申,简单来说有很多基本SQL语句不需要写,极大的简化的代码量。
1. 检查准备配置
PS:注解的方式操作只需要在接口类注解,且需要Bean对象即可,(提前在MySQL创建一个数据库,以及数据表)
编写前的准备工作
1.1检查并删除禁用数据库扫描装载(没有的不用动)
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
1.2配置MySQL数据库连接
在全局配置文件 application.properties 中:
root为用户名,123456为密码,设置成你自己设置的
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456
1.3确保MySQL有这个数据库
这里用Navicat工具连接数据库并创建springboot数据库
1.4在springboot数据库中创建一张用户表,用于CRUD操作
注意:这里的id是字段是设置为自动递增的,看参数设置,最后保存为 t_user 表名
2. Mapper 使用注解操作MySQL
2.1创建Bean类
在com包下新建bean包,创建User类,用于 Srping boot 自动创建或解析的实体对象
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
public class User {
private Integer id;
private String userName;
private String pwd;
}
2.2 @Mapper 注解创建 Mapper 接口
在com包下新建mapper包,并创建UserMapper接口操作数据:
import com.bean.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserMapper {
@Insert("INSERT INTO t_user(id, user_name, pwd) values(#{id}, #{userName}, #{pwd})")
int insertUser(User user);
@Select("SELECT * FROM t_user")
List<User> getAllUser();
@Delete("DELETE FROM t_user WHERE id = #{id}")
int deleteUserById(Integer id);
@Update("UPDATE t_user SET pwd = #{pwd} WHERE user_name = #{userName}")
int updatePasswordByUserName(User user);
}
2.3 在测试类中调用UserMapper对象并运行测试类
MybatisTests:
import com.bean.User;
import com.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisTests {
@Autowired
private UserMapper userMapper;
@Test
public void insertUser() {
User user = new User();
user.setUserName("bin");
user.setPwd("bin");
int resposeCode = userMapper.insertUser(user);
System.out.println("响应码:" + resposeCode);
}
@Test
public void selectUserList() {
for (User user : userMapper.getAllUser()) {
System.out.println(user);
}
}
}
2.4 解决实体类与数据表字段命名(驼峰与下划线)的映射
在全局配置文件中配置:
#解决实体类与数据表字段命名(驼峰与下划线)的映射
mybatis.configuration.map-underscore-to-camel-case=true
再次运行 MybatisTests 测试类,解决问题:
3. Mapper XML配置操作MySQL
3.1 配置 MyBatis XML映射文件路径
PS:我们在编写XML映射文件后,Spring Boot 并无从知晓,所以无法扫描到该自定义编写的XML配置文件,还必须在全局配置文件 application.properties 中添加MyBatis 映射文件路径配置,同时需要添加实体类别名映射路径。
XML 配置文件写到 resources,后期我们在 resources 下创建 mapper 文件夹存放 XML Mapper文件
#配置 MyBatis 的XML配置文件路径, resources:
mybatis.mapper-locations=classpath:mapper/*.xml
#配置XML映射文件中指定的实体类别名路径
mybatis.type-aliases-package=com.bean
3.2 创建 Mapper 接口文件
在 com.mapper 包下创建 UserXMLMapper 接口:
import com.bean.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserXMLMapper {
int insertUser(User user);
List<User> getAllUser();
int deleteUserById(Integer id);
int updatePasswordByUserName(User user);
}
3.3 创建XML SQL 映射文件
在 resources 下创建 mapper 映射的文件夹,之前在全局配置文件中配置过,必须对应名字,以及对应的Bean 实体类映射全别名也需要是指定位置
在 resources 资源文件夹的 mapper 文件夹下新建 XML 文件:
XML文件名可以自己定义,最好统一和Mapper接口文件一直名字,UserXMLMapper
<?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.mapper.UserXMLMapper">
<!-- 插入 -->
<insert id="insertUser">
INSERT INTO t_user (user_name, pwd) values (#{userName}, #{pwd})
</insert>
<!-- 查询 -->
<select id="getAllUser">
SELECT * FROM t_user
</select>
<!-- 删除 -->
<delete id="deleteUserById">
DELETE FROM t_user WHERE id = #{id}
</delete>
<!-- 更新 -->
<update id="updatePasswordByUserName">
UPDATE t_user SET pwd = #{pwd} WHERE user_name = #{userName}
</update>
</mapper>
解释:
- mapper 标签为主标签。
- namespace 属性:翻译是命名空间的意思,也就是指定对应的Mapper接口文件全限定名。
- 二级子标签:一般对应要写的语句类型即可。
- id 属性:对应指定的命名空间Mapper接口文件中的方法名。
3.4 另外创建测试类,测试 MyBatis XML
MybatisXMLTests :
import com.bean.User;
import com.mapper.UserXMLMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisXMLTests {
@Autowired
private UserXMLMapper userXMLMapper;
@Test
public void insertUser() {
User user = new User();
user.setUserName("bin");
user.setPwd("bin");
int resposeCode = userXMLMapper.insertUser(user);
System.out.println(resposeCode);
}
@Test
public void selectUserList() {
for (User user : userXMLMapper.getAllUser()) {
System.out.println(user);
}
}
}
运行 MybatisXMLTests 测试类 IDEA控制台输出结果和SQL数据库:
4. Spring Boot 整合 JPA 操作 MySQL
4.1 在 pom.xml 中引入 JPA 依赖
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
IDEA刷新 maven 依赖仓库:
4.2 配置 Bean User 实体类JPA注解
为了方便,直接在已有的 User 类中进行修改 JPA 注解
-
@Entity 注解:标记为 JPA Bean 类,注解在类名。
-
@Table 注解:指定对应的数据库表名,注解在类名。
-
@Id 注解:在数据表表中 自动递增的键字段,注解在成员变量,(在指定了表名后必须指定的注解)。
- 省略 @Table 注解 :在 @Entity 注解中直接设置表名 @Entity(name = "table_name"),而不需要 @Table(name = "table_name") 。
-
@GeneratedValue(strategy= GenerationType.IDENTITY) :主键生成策略。
- @Column 注解:@Column(name = "对应数据表列名") 数据表列名映射关系。
import jakarta.persistence.*;
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
//@Entity
//@Table(name = "t_user")
@Entity(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略
@Column(name = "id")
private Integer id;
@Column(name = "user_name")
private String userName;
@Column(name = "pwd")
private String pwd;
}
4.3 编写接口继承实现接口 JpaRepository<BeanType, IndexType>,自定义查询语句
- JpaRepository<BeanType, IDType> :bean 为操作数据表的实体类型,Id 为主键索引类型。
PS吐槽:这里编写接口继承,如果实在点说其实算是实现接口。
在com包下新建repository包并建立接口UserRepository:
import com.bean.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
//这里直接省略,自定义的查询
}
4.4 编写测试类,对JPA 接口自带CRUD方法操作数据表进行测试
MybatisJPATests :
import com.bean.User;
import com.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisJPATests {
@Autowired
private UserRepository userRepository;
@Test
public void insertUser() {
User user = new User();
user.setUserName("bin");
user.setPwd("bin");
User saveUser = userRepository.saveAndFlush(user); //插入直接用保存并刷新方法
System.out.println(saveUser);
}
@Test
public void findAll() {
List<User> list = userRepository.findAll(); //查询全部
for (User user : list) {
System.out.println(user);
}
}
@Test
public void deleteById() {
userRepository.deleteById(999); //根据表主键id进行删除
}
@Test
public void update() {
User user = new User();
user.setId(9); //对象中通过ID逐渐区分对象,进行更新用户名或密码(成员)
user.setUserName("bin");
user.setPwd("updateBinPwd");
userRepository.saveAndFlush(user); //更新其实就是保存方法,只不过ID值需要指定
}
}
注意:
- 根据数据库设置的键值唯一性质,saveAndFlush()保存方法和插入方法一致,只不过在更新时指定键值。
- 有些方法没有返回的结果码。
运行 MybatisJPATests 测试类 ,仅执行 insertUser() 方法再执行 updateUserById() 方法的执行结果:
4.5 自定义 CRUD SQL语句
直接修改接口 UserRepository
import com.bean.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional //直接注解在接口上,确保事物完整性
public interface UserRepository extends JpaRepository<User, Integer> {
@Modifying
@Query(nativeQuery = true, value = "INSERT INTO t_user (user_name, pwd) values (:#{#user.userName}, :#{#user.pwd})")
int insertUser(@Param("user") User user);
@Query(nativeQuery = true, value = "SELECT * FROM t_user")
List<User> getAllUser();
@Modifying
@Query("DELETE FROM t_user WHERE id = :id")
int deleteUserById(@Param("id") Integer id);
@Modifying
@Query(nativeQuery = true, value = "UPDATE t_user SET user_name = :#{#user.userName}, pwd = :#{#user.pwd} WHERE id = :#{#user.id}")
int updatePasswordById(@Param("user") User user);
}
注解:
- @Transactional :事物机制,确保数据在增删改的正确性。(可以注解在接口上,也可以注解在方法上)。
- @Modifying :注解为增删改 SQL 语句方法。
- @Query :意为查询语句
- nativeQuery = true :使用原生 SQL 语句。
- value = "" :编写SQL语句的地方。
- @Param :对参数属性进行设置 @Query 注解中SQL语句调用的变量名,在SQL中调用时使用英文冒号调用 :param
- 不用 @Param 注解对参数别名 :可以使用序号调用,从1~N 的参数个数,在@Query注解中调用时使用英文问号调用 ?1 或 ?2 或 ?3,序号对应这声明参数时的位置,最小从1开始。注意的是在@Query注解中调用时不能把序号弄反,如:@Query("?3 ?1 ?2"),这是不允许的,调用顺序必须跟着参数声明的顺序!(PS: 这种方式我并不推荐,在后期维护时容易混乱)。
- SpEL 表达式调用实体类参数:格式 :#{#param.属性名}
4.5.1 编写测试类,进行JPA自定义SQL语句操作MySQL
MybatisJPA_DIY_SQL_Tests :
import com.bean.User;
import com.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisJPA_DIY_SQL_Tests {
@Autowired
private UserRepository userRepository;
@Test
public void inserUser() {
User user = new User();
user.setUserName("binJpa");
user.setPwd("binJpa");
int resultCode = userRepository.insertUser(user);
System.out.println(resultCode);
}
@Test
public void selectAllUser() {
List<User> list = userRepository.getAllUser();
for (User user : list) {
System.out.println(user);
}
}
@Test
public void deleteById() {
int resultCode = userRepository.deleteUserById(999);
System.out.println(resultCode);
}
@Test
public void updateUserById() {
User user = new User();
user.setId(12);
user.setUserName("binJpa");
user.setPwd("updateBinJpa");
int resultCode = userRepository.updatePasswordById(user);
System.out.println(resultCode);
}
}
运行 MybatisJPA_DIY_SQL_Tests 测试类,仅执行 insertUser() 方法再执行 updateUserById() 方法的执行结果:
四、Redis 缓存(铺垫) | 编写接口 | 视图技术(Thymeleaf)
0. PS
注意:仅为第五节做铺垫。
理解程序在内存中的缓存,以及在读取后写入缓存,当写入缓存后,在数据库这些数据变化后,我们的缓存必须被更新或者删除问题,否则可能会出现缓存BUG。Spring Boot 有默认自带缓存,以及第三方允许的缓存框架依赖程序,其实我们的Spring Boot从自带缓存,到依赖程序缓存的转换,Spring Boot都会自动检测定位使用哪种缓存程序。
为了更方便的测试出效果,我们采用如下方法:
- 首先编写接口(控制器),需要的注解了解,以及请求参数的获取映射。
- 视图技术,使用 Thymeleaf 依赖,主要用于编写网页模板测试GET和POST请求参数的区别。(这里不涉及 Thymeleaf 表达式的讲解)
- 运行 Spring Boot ,通过浏览器来访问接口来操作数据库,操作数据使用 JPA 方式,我们在IDEA 控制台中可以查看 Redis SQL语句查询执行显示,来观察 Redis 是否成功运用而避免缓存BUG。
1. 编写接口 (控制器)
在 com.controller 包下创建控制器类 ThymeleafController :
import com.bean.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
@Controller //指定控制器类
@RequestMapping("/test") //指定当前类控制器全局地址,之后在每个(方法)接口上都是连接着这个主路径
public class ThymeleafController {
@GetMapping("") //设置为根路径,进入 Thymeleaf 视图技术使用的 test.html 模板测试之后的 GET 请求和 POST 请求方法
public String testIndex() {
//如果且当有的情况,返回的是 resources/templates 静态资源下的 .html 文件名称
return "thymeleaf";
}
//————————— 测试方法 <链接参数与请求体参数> 定义参数获取问题 —————————>
@GetMapping("/get") //测试设置了 request 和 response 对象参数
public void handleGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().println(request.toString()); //打印请求对象,仅作测试
response.getWriter().println("Test method params HttpServletRequest & HttpServletResponse");
}
@GetMapping("/get/{userName}/{pwd}")
//通过括号 {} 的方式拿取 URL 设置 <路径> 参数,@PathVariable 注解传值映射到方法中
// public void testGetPath(@PathVariable("userName") String userName, @PathVariable("pwd") String pwd, HttpServletResponse response) throws IOException {
// response.getWriter().println("userName == " + userName + "\n" + "pwd == " + pwd);
// }
//通过 @ModelAttribute 把路径参数属性映射到实体类上
public void testGetPath(@ModelAttribute User user, HttpServletResponse response) throws IOException {
response.getWriter().println("userName == " + user.getUserName() + "\n" + "pwd == " + user.getPwd());
}
//测试 @RequestParam 注解拿取 URL | <请求体 Body> 中的请求参数 (非 JSON 格式)
@RequestMapping(value = "/params", method = {RequestMethod.GET, RequestMethod.POST}) //设置可接收两种请求方法
public void requestParams(@RequestParam("param1") String param1, @RequestParam("param2") String param2, HttpServletResponse response) throws IOException {
response.getWriter().println("param1 == " + param1 + "\n" + "param2 == " + param2);
}
//测试 无注解参数,SpringBoot 自动将请求参数 (非 JSON 格式) 映射到实体类中
@RequestMapping(value = "/instance1", method = {RequestMethod.GET, RequestMethod.POST}) //设置可接收两种请求方法
public void postInstance1(User user, HttpServletResponse response) throws IOException {
response.getWriter().println(user.toString());
}
//post请求测试 @RequestBody 注解将请求体 JSON 参数映射到实体类中
@PostMapping(value = "/instance2", consumes = {"application/json"}) //设置只接收 json 格式数据
public void postInstance2(@RequestBody User user, HttpServletResponse response) throws IOException {
response.getWriter().println(user.toString());
}
}
注解解释:
- 定义控制器类:
- @RestController :它将类中的方法的返回值直接转换为 JSON 或 XML (HTML),并返回给客户端。
- @Controller:它将类中的方法的返回值返回纯文本数据格式。
- 特殊条件:在这里使用了视图技术,需要使用 @Controller 注解才能引导检索到静态模板页面文件,也就是方法的返回值字符串当成定位到 Thymeleaf 模板页面的 路径字符串,并返回给客户端。
- @RequestMapping:设置请求 URL 地址
- 定义在类上时:当前类中所有的方法上请求的接口链接都隶属于类上的。
- 定义在方法上:定义设置的接口地址处理的逻辑。
- 在一个方法上允许多种请求协议时:必须使用 @RequestMapping ,而不是在一个方法上定义多个其它的 Mapping 。
- 请求接口路径(定义在处理方法上):
- @GetMapping:为 GET 协议,设置请求 URL 地址,规范用于请求数据。
- @PostMapping:为 POST 协议,设置请求 URL 地址,规范用于提交数据。
- @PutMapping:为 PUT 协议,设置请求 URL 地址,规范用于更新数据。
- @DeleteMapping:为 DELETE 协议,设置请求 URL 地址,规范用于删除数据。
- value :URL 接口地址,当只有一个字符串 URL 地址时,无需显示设置 value。
- method:可以同时允许当前 URL 处理逻辑多种请求方法。
- consumes:设置接受的请求参数内容。
- 获取请求参数(定义在方法参数上):
- @PathVariable:用于获取 URL 请求路径括号 {} 内设置的表达式中的变量名传递数据映射到方法参数上。
- @ModelAttribute:直接将路径请求参数映射到实体类上。
- @RequestParam:用于获取 URL 或者 请求体 Body 中的键值对数据(非 JSON 数据),通过设置的请求参数键名获取并传递映射到方法参数上。
- @RequestBody:用于将请求体中 JSON 数据 映射到实体类Bean对象上。
2. 视图技术(Thymeleaf)
2.1 我们需要在 pom.xml 中引入依赖
PS:如果有就不用了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
引入后别忘了刷新 maven 仓库:
2.2 在全局配置文件 application.propertis 中配置
#———————— Thymeleaf 模板配置 ——————————
#启用模板缓存,默认开启 true,开发中方便调试设置为 false ,上线后还原为 true
spring.thymeleaf.cache=false
#模板页面编码,默认 UTF-8
#spring.thymeleaf.encoding=UTF-8
#模板页面模式,默认 HTML5
#spring.thymeleaf.mode=HTML5
#模板页面名称后缀,默认 .html
#spring.thymeleaf.suffix=.html
2.3 编写模板 HTML
在 resources 资源文件夹下创建 templates 文件夹,然后在 templates 下创建 thymeleaf.html HTML文件:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf</title>
</head>
<body>
<div align="center">
<p>接口:/test/get 协议:GET</p>
<form action="/test/get" method="get">
<input type="submit" value="测试跳转">
</form>
</div>
<hr>
<div align="center">
<p>接口:/test/get/{userName}/{pwd} 协议:GET</p>
<form action="/test/get/DIY_userName/DIY_pwd" method="get">
<input type="submit" value="路径方式传值">
</form>
</div>
<hr>
<div align="center">
<p>接口:/test/params 协议:GET</p>
<form action="/test/params" method="get">
<div>
<input type="text" name="param1"><br>
<input type="text" name="param2">
</div>
<input type="submit" value="GET请求参数传值">
</form>
</div>
<div align="center">
<p>接口:/test/params 协议:POST</p>
<form action="/test/params" method="post">
<div>
<input type="text" name="param1"><br>
<input type="text" name="param2">
</div>
<input type="submit" value="POST请求体参数传值">
</form>
</div>
<hr>
<div align="center">
<p>接口:/test/instance1 协议:POST</p>
<form action="/test/instance1" method="post">
<div>
<input type="text" name="userName"><br>
<input type="text" name="pwd">
</div>
<input type="submit" value="非JSON数据解析映射Bean对象">
</form>
</div>
<script type="text/javascript">
function submitJSON() {
const userName = document.getElementById("userName").value;
const pwd = document.getElementById("pwd").value;
const params = JSON.stringify({
userName : userName,
pwd : pwd
});
const ajax = new XMLHttpRequest();
ajax.open('POST', 'http://localhost:8080/test/instance2', true);
ajax.setRequestHeader('Content-Type', 'application/json'); //数据内容
ajax.onreadystatechange = () => {
if (ajax.readyState === XMLHttpRequest.DONE) { //请求完成
if (ajax.status === 200) {
console.log(ajax.responseText); //处理服务器响应
} else {
console.error('请求失败: ' + ajax.status); //处理请求失败的情况
}
}
};
ajax.send(params); //传递参数发送请求
}
</script>
<div align="center">
<p>接口:/test/post/instance2 协议:POST PS: 响应数据打印在控制台</p>
<div>
<input type="text" id="userName"><br>
<input type="text" id="pwd">
</div>
<input type="button" value="JSON数据解析映射Bean对象" onclick="submitJSON()">
</div>
</body>
</html>
我:为了更好的观察体验,直接不优化抽象代码,直观看个体即可(我的借口)!
<html lang="en" xmlns:th="http://www.thymeleaf.org"> :引入 Thymeleaf 命名空间。
th:text
用于处理p
标签体的文本内容。该模板文件直接在任何浏览器中正确显示,浏览器会自动忽略它们不能理解的属性th:text
。但这不是一个真正有效的 HTML5HTML5HTML5 文档,因为 HTML5 规范是不允许使用th:*
这些非标准属性的。我们可以切换到 Thymeleaf 的data-th-*
语法,以此来替换th:*
语法简单来说就是可以使用 Thymeleaf 表达式,但这里不使用
3. 运行 Spring Boot 并测试(学习编写接口和视图技术)
浏览器访问:(进入测试网页)
http://localhost:8080/test
返回 thymeleaf.html 网页:
测试结果:
五、基于注解的 Redis 缓存(优化查询)
PS
。。。。巴拉拉,根据以上第四节的认识,我们就快速开始进行 Redis 缓存的实验。
前面说了,Spring Boot 自带缓存到 依赖程序缓存的支持。
1. 开启 Spring Boot 缓存管理注解支持
在主入口 @SpringBootApplicatioon 的类上,加入如下注解
@EnableCaching //开启 Spring Boot 基于注解的缓存管理支持
2. Spring Boot 自带缓存 Simple
非常重点:只要不引入和配置 Spring Boot 支持的缓存程序,会自动使用自带的缓存。相反,当引入和配置指定的缓存程序后,自动接入缓存程序!!!!
包含支持以下9种依赖缓存程序:
- Generic
- JCache
- EhCache 2.x
- Hazeicast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple :这个即为默认自带缓存,缓存使用的相关对象为 ConcuurentHashMap 。
PS:我们直接延用之前写的 JPA UserRepository 接口操作数据库,增加一个新的查询语句,因为集合对象对缓存操作有所弊端,所以增加一个单个实体查询的。
import com.bean.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional //直接注解在接口上,确保事物完整性
public interface UserRepository extends JpaRepository<User, Integer> {
@Query(nativeQuery = true, value = "SELECT * FROM t_user")
List<User> getAllUser();
@Query(nativeQuery = true, value = "SELECT * FROM t_user WHERE id == #{id}}")
User selectUserById(Integer id);
@Modifying
@Query(nativeQuery = true, value = "INSERT INTO t_user (user_name, pwd) values (:#{#user.userName}, :#{#user.pwd})")
int insertUser(@Param("user") User user);
@Modifying
@Query("DELETE FROM t_user WHERE id = :id")
int deleteUserById(@Param("id") Integer id);
@Modifying
@Query(nativeQuery = true, value = "UPDATE t_user SET user_name = :#{#user.userName}, pwd = :#{#user.pwd} WHERE id = :#{#user.id}")
int updatePasswordById(@Param("user") User user);
}
3. 开启 JPA SQL 语句操作数据库时打印控制台
在全局配置文件 application.properties 中进行加入如下:
#使用JPA进行数据库SQL查询语句时进行打印显示到控制台
spring.jpa.show-sql=true
4. 编写 service 服务层控制 UserRepository 接口 (缓存逻辑)
PS:目的是后期我们在服务层进行管理控制缓存,无需改动原代码。
在 com 包下 新建 services 包下创建 UserService 服务类:
- @Service :标记类为业务层服务注解。
import com.bean.User;
import com.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@CacheConfig(cacheNames = UserService.mainName) //指定当前类全局缓存空间命名, 并自动开启当前服务类操作的对象全部SQL语句进行缓存管理
public class UserService {
//定义静态缓存空间名属性
public static final String mainName = "com.services.UserService";
//—————————————————————————————— 特别注意 ————————————————————————————
public static final String userListKey = "'userList'"; //当使用字符串时做key时, 需要使用单引号引起来
@Autowired
private UserRepository userRepository;
//用户列表,unless 为 true 时则不进行缓存, #result 固定关键字为方法返回结果, 缓存的是返回对象
@Cacheable(key = userListKey, unless = "#result == null")
public List<User> getUserList() {
return userRepository.getAllUser();
}
//通过 id 作为存储的键, unless 为 true 时则不进行缓存, #result 固定关键字为方法返回结果, 缓存的是方法返回的对象
@Cacheable(key = "#id", unless = "#result == null") //缓存单个用户对象
public User selectUserById(Integer id) {
return userRepository.selectUserById(id);
}
//当插入新的对象, 用户列表缓存直接删除指定key列表, 无需方法返回值对比
@CacheEvict(key = userListKey)
public String insertUser(User user) {
return user.toString() + " 插入结果码:" + userRepository.insertUser(user);
}
//更新 resCode 为 1 真的时候,才更新, 通过指定的 key 更新, 更新对象为方法返回值, 结果不为 null 时更新
@CachePut(key = "#user.id", unless = "#result == null") //更新缓存单个用户对象
@CacheEvict(key = userListKey) //当更新的对象,用户列表缓存直接删除
public User updateUserById(User user) { //通过id进行更新行数据
int resCode = userRepository.updatePasswordById(user);
if (resCode != 1) user = null;
return user;
}
//清除多个指定 key 的缓存数据, @CacheEvict 注解不会根据方法返回值判断
//可同时指定缓存空间,但只有一个缓存空间在类设置了,所以无需设置
@Caching(evict = {@CacheEvict(key = "#id"),
@CacheEvict(key = userListKey)})
public String deleteUserById(Integer id) {
return "删除id为 " + id + " 用户的结果码:" + userRepository.deleteUserById(id);
}
}
缓存注解解释:
注解 | 描述 | 示例 |
@EnableCaching | 开启 Spring Boot 基于注解的缓存管理支持 | 一般直接应用于主入口 @SpringBootApplication 类上 |
@Cacheable |
| |
@CachePut |
| 注意是通过方法的执行返回结果更新数据的 |
@CacheEvict |
特有属性:
| 清除指定空间全部缓存: |
@Caching |
| |
@CacheConfig |
| |
缓存注解属性解释:
属性名 | 说明 |
cacheNames 或 value | 指定缓存空间的名称,必配属性,二选一 |
key | 指定缓存数据的key,不设置就默认使用方法参数列表,(SpEL 表达式) |
condition | 指定条件为 true 时,才对数据进行缓存,(SpEL 表达式) |
unless | 指定条件为 true 时,不进行数据缓存,(SpEL 表达式) |
sync | 对数据缓存时是否使用异步模式,默认 false |
keyGenerator | 指定缓存数据的 key 生成器,与 key 属性二选一 |
cacheManager | 指定缓存管理器 |
cacheResolver | 指定缓存解析器,与 cacheManager 属性二选一 |
缓存注解属性中SpEL表达式解释:
名称 | 描述 | 示例 |
ArgumentName | 当前方法参数列表,使用 # 号调用:
| |
result | 当前方法返回的结果类型数据 | #result |
caches | 当前被调用方法的缓存列表 | #root.caches[0].name |
args | 当前被表用方法的参数列表 | #root.args[0] |
methodName | 当前被调用的方法名 | #root.methodName |
method | 当前被调用的方法 | #root.method.name |
target | 当前被调用的目标对象实例 | #root.target |
targetClass | 当前被调用的目标对象的类 | #root.targetClass |
5. 编写接口(控制器)访问接口来操作数据库
为了方便,CRUD操作全部支持 GET 协议,之后通过浏览器进行接口访问
在 com.controller 包下创建 RedisJPA_API_Controller 控制器:
package com.controller;
import com.bean.User;
import com.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.annotation.RequestScope;
import java.util.List;
@RestController
@RequestMapping("/api") //指定当前类控制器全局地址,之后在每个(方法)接口上都是连接着这个主路径
public class RedisJPA_API_Controller {
@Autowired
private UserService userService;
@GetMapping("/get") //查询用户列表
public List<User> getUserList() {
return userService.getUserList();
}
@GetMapping("/get/{id}") //查询单个用户
public User selectUserById(@PathVariable("id") Integer id) {
return userService.selectUserById(id);
}
//插入用户, 规范使用 POST 协议
@RequestMapping(value = "/insert/{userName}/{pwd}", method = {RequestMethod.GET, RequestMethod.POST})
public String insertUser(@ModelAttribute User user) {
return userService.insertUser(user);
}
//更新用户, 规范使用 PUT 更新协议
@RequestMapping(value = "/update/{id}/{userName}/{pwd}", method = {RequestMethod.GET, RequestMethod.PUT})
public User updateUserById(@ModelAttribute User user) {
return userService.updateUserById(user);
}
//删除用户, 规范使用 DELETE 删除协议
@RequestMapping(value = "/delete/{id}", method = {RequestMethod.GET, RequestMethod.DELETE})
public String deleteUserById(@PathVariable("id") Integer id) {
return userService.deleteUserById(id);
}
}
6. 体验无缓存 SQL 查询操作
6.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解先注释掉 :
6.2 运行我们的 Spring Boot 项目,然后打开我们的浏览器进行访问接口
获取用户列表:
http://localhost:8080/api/get
6.3 查看 IDEA 控制台,多次刷新浏览器查询用户列表
我们发现每刷新一次都会执行 SQL 查询语句 JPA 操作查询数据库,恭喜你体验成功!
7. 体验缓存 SQL 查询操作
7.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解的注释进行还原 :
7.2 正式 CRUD 体验缓存逻辑
运行 Spring Boot 项目,浏览器链接到用户列表接口:
http://localhost:8080/api/get
现在不管你刷新访问多少次这个接口,都只会从缓存中拿取第一次 SQL 语句查询 MySQL 时的数据,在 IDEA 控制台查看显示的 SQL 查询次数:
7.3 体验缓存更新
我们来尝试插入一个用户,访问插入用户路径参数接口:
http://localhost:8080/api/insert/newUser/newUser
再次访问用户列表接口:
http://localhost:8080/api/get
因为插入新数据而指定的缓存被删除,然后重新进行查询一次并再一次存入缓存:
其它接口自己操作试试吧~
8. 缓存小总结
- 对查询进行缓存:注意为 null 时的问题。
- 注意在进行增删改时:需要及时的更新内存中的缓存,避免缓存 BUG 。
- 注意对于集合对象数据:注意缓存的 key 问题,key 是单向的,并不能针对集合中元素的更新,因为没有对集合中单个元素进行 key 的指向。
- 注意默认 key 问题:未指定 key 默认使用方法中参数值单个或一组进行作为 key 值。
- 自定义的 String 作为 key 值:需要有单引号引起来 String listKey = "'myKey'" 。
9. 引入 Redis 缓存转变
PS:其实前面已经说过了,从默认缓存,到依赖程序缓存,Spring Boot 只要引入依赖并进行配置文件配置,就能直接从默认缓存转换过来!
注意:请提前在电脑装好 Redis 程序,这里就不掩饰了。
9.1 添加 Redis 依赖
在 pom.xml 中添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
刷新 maven 仓库更新依赖:
9.2 配置 Redis 密码 (可跳过)
PS:设置密码 为了安全一定要设,而且这里如果不绑定 IP 也不设密码的话,Redis 是默认保护模式,只能本地主机访问,不允许其他 IP 访问。
9.3 配置文件连接 Redis
在全局配置文件 application.properties 中配置:
#———————— Redis ——————————
#服务器地址
spring.data.redis.host=127.0.0.1
#服务器端口
spring.data.redis.port=6379
#Redis 服务器连接密码(默认未设置为空), 默认保护模式,只能本地主机访问,有需求需改密码
spring.data.redis.password=
#基于注解的 Redis 统一设置缓存数据有效时间, 单位毫秒(mm)
spring.cache.redis.time-to-live=100000
9.4 Redis 对 Bean 实体类实现序列化接口
PS:Redis 在缓存时,缓存的数据实体类需要进行实现序列化接口,我们的缓存数据对象只有 User ,所以,我们需要对 User 实体类进行实现序列化接口。
修改 com.bean 包下的 User 实体类实现序列化接口:
import jakarta.persistence.*;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Data
@Component
//@Entity
//@Table(name = "t_user")
@Entity(name = "t_user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略
@Column(name = "id")
private Integer id;
@Column(name = "user_name")
private String userName;
@Column(name = "pwd")
private String pwd;
}
9.5 体验 Redis 缓存
PS:我们根据以上步骤配置完全后,就已经完成了从自带缓存 Simple 到 Redis 转换。不过这里我们需要借助 Redis 缓存管理工具进行查看缓存。
通过浏览器访问接口:
http://localhost:8080/api/get
http://localhost:8080/api/get/1
Redis Desktop Manager 桌面管理程序查看缓存:
命令查看缓存列表 (Windows) :
1. 进入 Redis 安装目录,打开 CMD 进入该目录。
2. 执行命令
redis-cli.exe -h <hostname> -p <port>
redis-cli.exe -h 127.0.0.1 -p 6379
3. 执行 keys * 查看全部的键缓存
keys *
六、jar & war 打包及部署
0. 配置 pom.xml 打包规则
0.1 Maven 打包插件
Spring Boot 项目进行 Jar 和 War 打包需要在 pom.xml 中加入 Mave 打包插件,不过我们在创建项目的时候自动的为我们配置了。
pom.xml 中 Maven 打包插件文件配置:
<!-- maven 打包插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
0.2 配置打包方式
默认是 jar 的打包方式,war 打包需要配置,以下是配置,在标签<project>下层配置 :
<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>jar</packaging>
0.3 配置可使用外部 Tomcat 部署
默认Spring Boot 默认使用内置 Tomcat 引擎,在标签<dependencies>下层配置:
<!-- (默认内置 Tomcat 引擎) 声明使用外部 Tomcat 部署 war -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!-- 声明两种可外部 Tomcat War 包部署、可内嵌 Tomcat Jar 包部署 -->
<scope>provided</scope>
</dependency>
0.4 配置打包输出命名 (项目名+版本号)
在打包时命名输出使用的是 <项目名+定义项目版本号> 命名的,可在标签<project>下层找到进行修改,我这就就默认了:
<groupId>com.example</groupId> <!-- 包名 -->
<artifactId>demo</artifactId> <!-- 定位 -->
<version>0.0.1-SNAPSHOT</version> <!-- 项目版本 -->
<name>demo</name> <!-- 项目名称 -->
<description>demo</description> <!-- 项目描述 -->
0.5 完整 pom.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version> <!-- Spring Boot 版本 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId> <!-- 包名 -->
<artifactId>demo</artifactId> <!-- 定位 -->
<version>0.0.1-SNAPSHOT</version> <!-- 项目版本 -->
<name>demo</name> <!-- 项目名称 -->
<description>demo</description> <!-- 项目描述 -->
<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>jar</packaging>
<properties>
<java.version>17</java.version> <!-- Spring Boot 版本对应的 JDK 版本 -->
</properties>
<dependencies>
<!-- (默认内置 Tomcat 引擎) 声明使用外部 Tomcat 部署 war -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!-- 声明两种可外部 Tomcat War 包部署、可内嵌 Tomcat Jar 包部署 -->
<scope>provided</scope>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- maven 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- Lombok 配置 -->
<excludes>
<exclude>
<groupId>org.project.lombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom.xml 配置更改了记得刷新 Maven 仓库配置。
1. 打包跳过测试文件
PS1:IDEA 跳过测试类文件打包,Maven --> Lifecycle --> test 点击 --> 点击上面【闪电图标】 --> test 按钮被划掉。
当前 Maven 依赖不支持配置跳过测试文件打包,所以我们在 IDEA 指定。
2. Jar 打包部署
2.1 配置 jar 打包方式
默认是 jar 的打包方式,war 打包需要配置,以下是配置,在标签<project>下层配置 :
<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>jar</packaging>
2.2 打包
PS:IDEA 打包,Maven --> Lifecycle --> package 双击。
打包完成后,jar 包存放在项目的 target 目录下。
2.3 打包报<编码UTF-8的不可映射字符>解决 (如没有可跳过)
PS:就是某些类文件设置的编码格式不是 UTF-8 问题,如下图更改,不过也能进入配置文件更改。
2.4 命令运行 Jar 包
PS:这里是 Windows 系统,我们用系统命令终端执行运行 jar 包,IDEA 的终端也行。
打开 CMD --> 系统命令进入你的 jar 包路径 --> 输入如下 java 命令运行 Jar 包:
java -jar demo-0.0.1-SNAPSHOT.jar
等待运行完成:
PS:我们可以看到内置引擎 Tomcat/10.1.19 版本,以及端口号 8080。
3. War 打包 Tomcat 部署
3.1 配置 war 打包方式
默认是 jar 的打包方式,war 打包需要配置,以下是配置,在标签<project>下层配置 :
<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>war</packaging>
3.2 主程序入口继承 SpringBootServletInitializer 类
PS:主程序入口继承 SpringBootServletInitializer 来支持外部 Tomcat 部署的 Servlet 支持。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
@EnableCaching //开启 Spring Boot 基于注解的缓存管理支持
@ServletComponentScan //开启基于注解方式 Servlet 组件扫描支持
/*
继承 SpringBootServletInitializer 类,以提供 Servlet 3.0 支持
重写 configure(builder) 方法,调用并返回 builder.sources(<SpringBootApplication.class 主程序入口类>); 提供Servlet初始化对象
*/
public class DemoApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(DemoApplication.class); //重写configure()方法为SpringBoot提供Servlet初始化对象
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("测试中文编码");
}
}
3.4 打包
PS:IDEA 打包,Maven --> Lifecycle --> package 双击。
打包完成后,jar 包存放在项目的 target 目录下。
4. Tomcat 部署 war 包 (Windows)
4.1 安装 Tomcat
PS:在部署 war 包前我们需要知道 war 包的需求版本,我们的 Spring Boot 版本以及对应 Java JDK 都是有对应版本的 Tomcat ,其实也就是年代原因,某些 Tomcat 版本不支持高版本的 Spring Boot 。
版本需求:
- Spring Boot 版本:3.2.3
- Tomcat :10
- JDK:19
资源在文章开头提供有,安装目录:
4.2 配置 Tomcat 环境变量
PS:Tomcat 在启动服务命令前,我们需要指定 Tomcat 的安装路径、JRE 运行路径、为了快速启动 Tomcat 还可设置 bin 启动脚本命令路径。
Tomcat 10 环境变量配置表 | |
变量名 | 说明 |
CATALINA_HOME | Tomcat 安装路径 |
JRE_HOME | JRE 路径,系统如果只装了 JDK 就是 JDK 路径即可 |
%CATALINA_HOME%\bin | 这一个一个引用变量值,为了快速启动 Tomcat 而配置的,直接使用启动命令。 请配置到 path 变量中。 |
4.3 运行 Tomcat
PS:把打包好的 war 包拷贝到 Tomcat 安装路径的 webapps 目录下,并重命名这个 war 包为你想要的一个链接主名。
我这里命名为 app.war :
打开终端 CMD 运行 Tomcat 的 bin 目录下的 startup.bat :
我这里配置了环境变量 Tomcat 的 bin 路径,所以直接运行
startup.bat
可以看到新增了个窗口运行 Tomcat:
浏览器访问 Tomcat :
http://localhost:8080/
4.4 浏览器访问项目
PS:使用 Tomcat 访问的链接是有变化的,我们在第一次运行Tomcat 解包 war 时,就会用 war 包的名字进行命名链接的主名。
可以看到 Tomcat 安装目录的 webapps 下就生成了 app 文件夹,这就是在重命名 war 包的目的:
通过浏览器访问,链接需要加自己定义<app>名字进行访问:
http://localhost:8080/app/test
4.5 ROOT 根路径去除定义的文件夹名头访问链接
PS:其实就是在 Tomcat 安装路径 webapps 目录下,把自己的项目的文件名改成 ROOT 即可。把原来的 ROOT 文件夹命名成其它的名字(随意)。
我这里因为有个 app.war 包,要是改动 app 文件夹名就又会重新生成一个 app 文件夹名的项目,所以我这里把 app.war 包先删除,再改动:
- 原来的 ROOT ---> tomcat
- 项目名 app ---> ROOT
现在关闭并重启我们的 Tomcat 服务。
访问我们的项目使用链接 :
http://localhost:8080/test