一、入门基础:快速搭建与核心认知
1. 环境准备
- JDK:推荐JDK 11+(Spring Boot 3.0+最低要求JDK 17,兼容性更优)。
- 构建工具:Maven 3.6+ 或 Gradle 7.0+,优先选择Maven(生态更成熟、资料更丰富)。
- 开发工具:IntelliJ IDEA(推荐旗舰版,自带Spring Boot插件)或 Eclipse(需安装Spring Tools Suite插件)。
- 辅助工具:Postman(接口测试)、Navicat(数据库管理)、Lombok插件(简化代码)。
2. 快速创建Spring Boot项目(3种方式)
方式1:Spring Initializr(官网可视化创建)
- 访问官网:https://start.spring.io/。
- 配置参数:
- Project:Maven/Gradle(选Maven)。
- Language:Java。
- Spring Boot:选择稳定版(如3.2.x,避免快照版)。
- Group/Artifact:定义项目包名(如group=com.example,artifact=demo)。
- Dependencies:勾选核心依赖(Web、Lombok、Spring Data JPA、MySQL Driver等)。
- 点击「Generate」下载压缩包,解压后用IDEA打开即可。
方式2:IDEA内置工具创建
- 打开IDEA → New Project → 选择「Spring Initializr」。
- 配置参数(同官网,无需访问网页,更便捷)。
- 勾选依赖后直接创建,IDEA自动加载依赖并初始化项目结构。
方式3:手动搭建(理解底层原理)
- 新建Maven项目,在pom.xml中添加Spring Boot父依赖(统一版本管理):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version> <!-- 稳定版 -->
<relativePath/>
</parent>
- 添加核心依赖(如Web依赖,用于开发接口):
<dependencies>
<!-- Web核心依赖(内置Tomcat、Spring MVC) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok(简化POJO类) -->
<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>
</dependencies>
- 编写主启动类(项目入口):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // 核心注解,包含3个关键注解
public class DemoApplication {
public static void main(String[] args) {
// 启动Spring Boot应用
SpringApplication.run(DemoApplication.class, args);
}
}
3. 核心注解解析(@SpringBootApplication)
@SpringBootApplication是复合注解,包含以下3个核心注解,无需单独声明:
- @SpringBootConfiguration:标记当前类为配置类,等价于@Configuration。
- @EnableAutoConfiguration:自动配置核心,根据pom依赖自动加载默认配置(如引入spring-boot-starter-web则自动配置Tomcat、Spring MVC)。
- @ComponentScan:自动扫描当前包及子包下的@Component、@Service、@Controller等注解的Bean,默认扫描主启动类所在包。
4. 第一个接口开发(Hello World)
- 编写Controller类(需在主启动类同级或子包下):
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 等价于@Controller + @ResponseBody(返回JSON数据)
@RequestMapping("/api") // 接口统一前缀
@Slf4j // Lombok注解,自动生成日志对象log
public class HelloController {
@GetMapping("/hello") // GET请求接口
public String hello(String name) {
log.info("收到请求,name:{}", name); // 日志输出
return "Hello, " + (name == null ? "Spring Boot" : name);
}
}
- 启动项目:运行DemoApplication的main方法,控制台显示「Started DemoApplication in xx seconds」表示启动成功。
- 测试接口:
- 浏览器访问:http://localhost:8080/api/hello?name=Java
- Postman发送GET请求,返回结果:Hello, Java
二、配置体系:灵活配置与多环境适配
Spring Boot的配置核心是「约定优于配置」,默认支持多种配置格式,优先级:命令行参数 > 系统环境变量 > application.yml > application.properties。
1. 配置文件格式(推荐YAML)
(1)application.properties(键值对格式,传统)
# 服务器配置
server.port=8080
server.servlet.context-path=/demo
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 自定义配置
app.name=spring-boot-demo
app.version=1.0.0
(2)application.yml(缩进格式,简洁高效,推荐)
YAML语法规则:缩进敏感(2个空格)、键值对用「: 」分隔、数组用「-」开头。
# 服务器配置
server:
port: 8080
servlet:
context-path: /demo
# 数据库配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 自定义配置
app:
name: spring-boot-demo
version: 1.0.0
author:
name: ZhangSan
age: 25
tags: [Java, SpringBoot, MySQL] # 数组写法
2. 自定义配置读取(3种方式)
方式1:@Value注解(适合单个配置)
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigController {
// 读取单个配置
@Value("${app.name}")
private String appName;
// 读取数组(默认用逗号分隔)
@Value("${app.tags:Java,Spring}") // 冒号后为默认值
private String[] appTags;
@GetMapping("/config")
public String getConfig() {
return "应用名称:" + appName + ",标签:" + String.join(",", appTags);
}
}
方式2:@ConfigurationProperties(适合批量配置,推荐)
- 编写配置类,绑定前缀为「app」的配置:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component // 注入Spring容器
@ConfigurationProperties(prefix = "app") // 绑定配置前缀
@Data // Lombok注解,自动生成getter/setter
public class AppConfig {
private String name;
private String version;
private Author author; // 嵌套对象
private String[] tags;
// 嵌套配置类
@Data
public static class Author {
private String name;
private Integer age;
}
}
- 使用配置类:
@RestController
public class ConfigController {
private final AppConfig appConfig;
// 构造器注入(Spring Boot推荐,无需@Autowired)
public ConfigController(AppConfig appConfig) {
this.appConfig = appConfig;
}
@GetMapping("/config2")
public String getConfig2() {
return "应用名称:" + appConfig.getName() +
",作者:" + appConfig.getAuthor().getName();
}
}
方式3:Environment对象(动态获取)
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigController {
private final Environment environment;
public ConfigController(Environment environment) {
this.environment = environment;
}
@GetMapping("/config3")
public String getConfig3() {
// 动态获取配置
String version = environment.getProperty("app.version");
Integer authorAge = environment.getProperty("app.author.age", Integer.class);
return "版本:" + version + ",作者年龄:" + authorAge;
}
}
3. 多环境配置(开发/测试/生产)
实际开发中需区分环境,Spring Boot支持通过「spring.profiles.active」激活不同环境。
(1)创建多环境配置文件
- 开发环境:application-dev.yml
- 测试环境:application-test.yml
- 生产环境:application-prod.yml
(2)配置示例(application-dev.yml)
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_dev?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
(3)激活环境(3种方式)
- 配置文件激活(application.yml中添加):
spring:
profiles:
active: dev # 激活开发环境
- 命令行激活(启动时添加参数):
java -jar demo.jar --spring.profiles.active=prod
- IDEA激活:Run/Debug Configurations → 配置Program arguments为「--spring.profiles.active=test」。
4. 配置加密(敏感信息保护)
生产环境中,数据库密码、密钥等敏感信息不能明文存储,可使用jasypt加密。
(1)添加依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
(2)配置加密密钥(生产环境用命令行传入,避免硬编码)
jasypt:
encryptor:
password: ${JASYPT_SECRET:defaultSecret} # 密钥,生产环境通过环境变量传入
(3)生成加密后的字符串(测试类中执行)
import org.jasypt.encryption.StringEncryptor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JasyptTest {
@Autowired
private StringEncryptor encryptor;
@Test
public void encrypt() {
String password = "123456"; // 明文密码
String encrypted = encryptor.encrypt(password);
System.out.println("加密后:" + encrypted); // 如:XxYyZz123...
}
}
(4)配置文件中使用加密后的字符串(用ENC()包裹)
spring:
datasource:
password: ENC(XxYyZz123...) # 加密后的密码
三、核心功能:Web开发与数据访问
1. Web开发核心技巧
(1)请求参数接收(5种常见场景)
- 路径参数(@PathVariable):
@GetMapping("/user/{id}")
public String getUserById(@PathVariable Long id) {
return "用户ID:" + id;
}
- 请求参数(@RequestParam):
@GetMapping("/user")
public String getUser(@RequestParam(required = false, defaultValue = "1") Long page,
@RequestParam String name) {
return "页码:" + page + ",姓名:" + name;
}
- 请求体(@RequestBody,接收JSON):
@Data // Lombok注解
public class UserDTO {
private String name;
private Integer age;
}
@PostMapping("/user")
public String addUser(@RequestBody UserDTO userDTO) {
return "新增用户:" + userDTO.getName() + ",年龄:" + userDTO.getAge();
}
- 请求头(@RequestHeader):
@GetMapping("/header")
public String getHeader(@RequestHeader String token) {
return "Token:" + token;
}
- Cookie(@CookieValue):
@GetMapping("/cookie")
public String getCookie(@CookieValue String sessionId) {
return "SessionId:" + sessionId;
}
(2)统一返回结果封装
避免接口返回格式混乱,定义统一响应类:
import lombok.Data;
@Data
public class Result<T> {
private int code; // 响应码(200成功,500失败)
private String msg; // 响应信息
private T data; // 响应数据
// 成功响应(带数据)
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
// 成功响应(无数据)
public static <T> Result<T> success() {
return new Result<>(200, "操作成功", null);
}
// 失败响应
public static <T> Result<T> fail(String msg) {
return new Result<>(500, msg, null);
}
}
使用示例:
@PostMapping("/user")
public Result<UserDTO> addUser(@RequestBody UserDTO userDTO) {
// 业务逻辑处理
return Result.success(userDTO);
}
返回JSON格式:
{
"code": 200,
"msg": "操作成功",
"data": {
"name": "ZhangSan",
"age": 25
}
}
(3)全局异常处理(@RestControllerAdvice)
统一捕获接口异常,避免返回404/500原始错误:
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice // 全局异常处理,作用于所有@RestController
public class GlobalExceptionHandler {
// 捕获空指针异常
@ExceptionHandler(NullPointerException.class)
public Result<Void> handleNullPointerException(NullPointerException e) {
return Result.fail("空指针异常:" + e.getMessage());
}
// 捕获参数绑定异常
@ExceptionHandler(IllegalArgumentException.class)
public Result<Void> handleIllegalArgumentException(IllegalArgumentException e) {
return Result.fail("参数错误:" + e.getMessage());
}
// 捕获所有未定义异常
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
return Result.fail("系统异常:" + e.getMessage());
}
}
(4)拦截器(Interceptor)
用于登录验证、日志记录等场景,步骤如下:
- 实现HandlerInterceptor接口:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
// 请求处理前执行(如登录验证)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if (token == null || token.isEmpty()) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"未登录,请先登录\"}");
return false; // 拦截请求,不继续执行
}
// 验证token有效性(实际开发中查询数据库或Redis)
log.info("token验证通过:{}", token);
return true; // 放行请求
}
// 请求处理后执行(如日志记录)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, Object modelAndView) throws Exception {
log.info("请求路径:{},响应状态码:{}", request.getRequestURI(), response.getStatus());
}
}
- 配置拦截器(实现WebMvcConfigurer):
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 配置类,替代XML配置
public class WebMvcConfig implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/api/login", "/api/hello") // 排除登录、测试接口
.excludePathPatterns("/static/**"); // 排除静态资源
}
}
2. 数据访问核心(MyBatis + Spring Data JPA)
(1)MyBatis整合(推荐,灵活可控)
- 添加依赖:
<!-- MyBatis整合Spring Boot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
- 配置application.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml # Mapper.xml文件路径
type-aliases-package: com.example.demo.entity # 实体类别名包
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰(如user_name → userName)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志
pagehelper:
helper-dialect: mysql # 数据库方言
reasonable: true # 合理化分页(页码<=0时查第一页,页码超出时查最后一页)
- 核心组件开发:
- 实体类(Entity):
import lombok.Data;
@Data
public class User {
private Long id;
private String userName; // 对应数据库user_name字段
private Integer age;
private String email;
}
- Mapper接口(注解版,简单SQL):
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.User;
import java.util.List;
@Repository // 标记为数据访问层Bean
public interface UserMapper {
// 查询所有用户
@Select("SELECT * FROM user")
List<User> listAll();
// 根据ID查询
@Select("SELECT * FROM user WHERE id = #{id}")
User getById(Long id);
// 新增用户
@Insert("INSERT INTO user(user_name, age, email) VALUES(#{userName}, #{age}, #{email})")
int insert(User user);
// 更新用户
@Update("UPDATE user SET user_name = #{userName}, age = #{age} WHERE id = #{id}")
int update(User user);
// 删除用户
@Delete("DELETE FROM user WHERE id = #{id}")
int delete(Long id);
}
- Mapper.xml(XML版,复杂SQL): 在resources/mapper目录下创建UserMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- 复杂查询:根据用户名模糊查询 + 年龄筛选 -->
<select id="listByCondition" resultType="com.example.demo.entity.User">
SELECT * FROM user
<where>
<if test="userName != null and userName != ''">
AND user_name LIKE CONCAT('%', #{userName}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
ORDER BY id DESC
</select>
</mapper>
对应Mapper接口添加方法:
List<User> listByCondition(User user);
- Service层(业务逻辑封装):
import org.springframework.stereotype.Service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.result.Result;
import jakarta.annotation.Resource;
import java.util.List;
@Service // 标记为业务层Bean
public class UserService {
@Resource // 注入Mapper(或用@Autowired)
private UserMapper userMapper;
// 查询所有用户
public List<User> listAll() {
return userMapper.listAll();
}
// 分页查询用户
public PageInfo<User> listPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize); // 分页插件生效
List<User> userList = userMapper.listAll();
return new PageInfo<>(userList); // 封装分页结果
}
// 新增用户
public int addUser(User user) {
// 业务逻辑校验(如用户名非空)
if (user.getUserName() == null || user.getUserName().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
return userMapper.insert(user);
}
}
- Controller层(接口暴露):
import com.example.demo.entity.User;
import com.example.demo.result.Result;
import com.example.demo.service.UserService;
import com.github.pagehelper.PageInfo;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/list")
public Result<List<User>> listAll() {
return Result.success(userService.listAll());
}
@GetMapping("/page")
public Result<PageInfo<User>> listPage(@RequestParam int pageNum, @RequestParam int pageSize) {
return Result.success(userService.listPage(pageNum, pageSize));
}
@PostMapping("/add")
public Result<Integer> addUser(@RequestBody User user) {
return Result.success(userService.addUser(user));
}
}
(2)Spring Data JPA整合(简化CRUD,适合快速开发)
- 添加依赖:
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
- 配置application.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update # 自动建表(create:删除重建,update:更新,none:禁用)
show-sql: true # 打印SQL日志
properties:
hibernate:
format_sql: true # SQL格式化输出
database-platform: org.hibernate.dialect.MySQL8Dialect # 数据库方言
- 核心组件开发:
- 实体类(Entity + JPA注解):
import jakarta.persistence.*;
import lombok.Data;
import java.io.Serializable;
@Data
@Entity // 标记为JPA实体
@Table(name = "t_user") // 对应数据库表名(默认类名小写)
public class User implements Serializable {
@Id // 主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增策略
private Long id;
@Column(name = "user_name", nullable = false, length = 50) // 字段配置(非空,长度50)
private String userName;
@Column(name = "age")
private Integer age;
@Column(name = "email", unique = true) // 唯一约束
private String email;
}
- Repository接口(继承JpaRepository,无需写SQL):
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.User;
import java.util.List;
@Repository
// JpaRepository<实体类, 主键类型>:自带CRUD、分页等方法
public interface UserRepository extends JpaRepository<User, Long> {
// 自定义查询方法(JPA自动解析方法名生成SQL)
List<User> findByUserNameLike(String userName); // LIKE查询:user_name LIKE %xxx%
List<User> findByAgeGreaterThan(Integer age); // 大于查询:age > xxx
User findByEmail(String email); // 等值查询:email = xxx
}
- Service层:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import jakarta.annotation.Resource;
import java.util.List;
@Service
public class UserService {
@Resource
private UserRepository userRepository;
// 分页查询
public Page<User> listPage(int pageNum, int pageSize) {
Pageable pageable = PageRequest.of(pageNum - 1, pageSize); // JPA页码从0开始
return userRepository.findAll(pageable);
}
// 自定义查询
public List<User> findByUserName(String userName) {
return userRepository.findByUserNameLike("%" + userName + "%");
}
// 新增/修改(save方法:主键为空则新增,不为空则修改)
public User saveUser(User user) {
return userRepository.save(user);
}
// 删除
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
四、高效开发技巧:简化代码与性能优化
1. Lombok深度使用(减少模板代码)
Lombok通过注解自动生成getter/setter、构造器、toString等代码,核心注解:
- @Data:组合注解(@Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor)。
- @NoArgsConstructor:生成无参构造器。
- @AllArgsConstructor:生成全参构造器。
- @RequiredArgsConstructor:生成带final/NonNull字段的构造器(适合构造器注入)。
- @Slf4j:生成日志对象(无需手动创建Logger)。
- @Builder:生成建造者模式(方便对象创建)。
示例(建造者模式):
import lombok.Builder;
import lombok.Data;
@Data
@Builder // 启用建造者模式
public class UserDTO {
private String name;
private Integer age;
private String email;
}
// 使用建造者创建对象
UserDTO userDTO = UserDTO.builder()
.name("ZhangSan")
.age(25)
.email("test@xxx.com")
.build();
2. 依赖注入最佳实践
- 优先使用构造器注入(Spring Boot推荐,避免循环依赖,代码更健壮):
@Service
public class UserService {
private final UserMapper userMapper;
private final RedisTemplate<String, Object> redisTemplate;
// 构造器注入(无需@Autowired,Spring自动识别)
public UserService(UserMapper userMapper, RedisTemplate<String, Object> redisTemplate) {
this.userMapper = userMapper;
this.redisTemplate = redisTemplate;
}
}
- 避免字段注入(@Autowired直接标注在字段上):
// 不推荐:可读性差,难以单元测试,可能出现循环依赖
@Autowired
private UserMapper userMapper;
3. 缓存优化(Redis整合)
频繁查询数据库会导致性能瓶颈,用Redis缓存热点数据。
(1)添加依赖:
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池(提升性能) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
(2)配置application.yml:
spring:
redis:
host: localhost # Redis地址
port: 6379 # 端口
password: # 密码(无则留空)
database: 0 # 数据库索引
lettuce: # 连接池配置(lettuce是Spring Boot默认客户端)
pool:
max-active: 8 # 最大连接数
max-idle: 8 # 最大空闲连接数
min-idle: 2 # 最小空闲连接数
max-wait: -1ms # 最大等待时间(-1表示无限制)
(3)缓存注解使用(简化缓存逻辑)
- 主启动类添加@EnableCaching:
@SpringBootApplication
@EnableCaching // 开启缓存支持
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- Service层使用缓存注解:
@Service
public class UserService {
@Resource
private UserMapper userMapper;
// 查询缓存:key为"user:id",缓存时间30分钟
@Cacheable(value = "user", key = "#id", unless = "#result == null")
public User getById(Long id) {
// 缓存未命中时执行该方法,结果存入Redis
return userMapper.getById(id);
}
// 更新缓存:修改数据后更新缓存
@CachePut(value = "user", key = "#user.id")
public User update(User user) {
userMapper.update(user);
return user;
}
// 删除缓存:删除数据后清除缓存
@CacheEvict(value = "user", key = "#id")
public void delete(Long id) {
userMapper.delete(id);
}
// 清除所有缓存
@CacheEvict(value = "user", allEntries = true)
public void clearCache() {
// 无需业务逻辑,仅清除缓存
}
}
(4)自定义Redis序列化(解决默认序列化乱码问题)
默认Redis序列化会将对象转为二进制,可读性差,自定义JSON序列化:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 字符串序列化器(key)
StringRedisSerializer keySerializer = new StringRedisSerializer();
// JSON序列化器(value)
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
// 配置缓存规则
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默认缓存时间30分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))
.disableCachingNullValues(); // 不缓存null值
// 创建缓存管理器
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
回复