缓存是一种提高系统性能的常用技术,它将频繁访问的数据存储在内存中,减少数据库访问次数。
1、Spring缓存抽象
Spring提供了缓存抽象,通过注解方式实现缓存功能:
@EnableCaching:启用缓存支持@Cacheable:在方法执行前检查缓存@CachePut:将方法返回值放入缓存@CacheEvict:清除缓存@Caching:组合多个缓存操作
2、缓存注意事项
- 合理设置缓存过期时间
- 对于频繁变更的数据慎用缓存
- 考虑缓存穿透、缓存雪崩等问题
- 大对象不适合缓存
3、实现步骤
添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 使用Redis作为缓存实现 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置Redis
yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
cache:
type: redis
redis:
time-to-live: 60000 # 缓存过期时间(毫秒)
启用缓存
java
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
使用缓存注解
java
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product getProductById(Long id) {
// 模拟数据库查询
System.out.println("查询数据库获取产品: " + id);
return new Product(id, "产品" + id, 100.0);
}
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
// 模拟更新数据库
System.out.println("更新产品: " + product.getId());
return product;
}
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) {
// 模拟删除数据库记录
System.out.println("删除产品: " + id);
}
}
测试案例
java
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.getProductById(id);
}
@PutMapping
public Product updateProduct(@RequestBody Product product) {
return productService.updateProduct(product);
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
}
4、SpringBoot Session共享实现
在分布式或集群环境中,需要实现Session共享以保证用户在不同服务器间切换时保持登录状态。
1、使用Redis实现Session共享
添加依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置Session存储:
yml
spring:
session:
store-type: redis
timeout: 1800 # Session过期时间(秒)
redis:
host: localhost
port: 6379
启用Redis Session:
java
@SpringBootApplication
@EnableRedisHttpSession
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Session操作:
java
@RestController
@RequestMapping("/session")
public class SessionController {
@GetMapping("/set")
public String setSession(HttpServletRequest request) {
HttpSession session = request.getSession();
session.setAttribute("user", "user123");
session.setAttribute("loginTime", LocalDateTime.now());
return "Session设置成功";
}
@GetMapping("/get")
public Map<String, Object> getSession(HttpServletRequest request) {
HttpSession session = request.getSession();
Map<String, Object> sessionData = new HashMap<>();
sessionData.put("sessionId", session.getId());
sessionData.put("user", session.getAttribute("user"));
sessionData.put("loginTime", session.getAttribute("loginTime"));
sessionData.put("creationTime", session.getCreationTime());
sessionData.put("lastAccessedTime", session.getLastAccessedTime());
return sessionData;
}
@GetMapping("/invalidate")
public String invalidateSession(HttpServletRequest request) {
HttpSession session = request.getSession();
session.invalidate();
return "Session已失效";
}
}
自定义Session配置
java
@Configuration
public class SessionConfig {
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
// 使用JSON序列化Session
return new GenericJackson2JsonRedisSerializer();
}
@Bean
public RedisOperationsSessionRepository sessionRepository(
RedisOperations<Object, Object> sessionRedisOperations) {
RedisOperationsSessionRepository sessionRepository =
new RedisOperationsSessionRepository(sessionRedisOperations);
// 设置Session过期时间
sessionRepository.setDefaultMaxInactiveInterval(1800);
return sessionRepository;
}
}
5、用户登录与缓存
用户登录实现Session共享:
java
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest request,
HttpServletRequest httpRequest) {
// 模拟用户验证
if ("admin".equals(request.getUsername()) && "123456".equals(request.getPassword())) {
HttpSession session = httpRequest.getSession();
session.setAttribute("currentUser", request.getUsername());
return ResponseEntity.ok("登录成功");
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
@GetMapping("/logout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "已退出登录";
}
@GetMapping("/currentUser")
public String currentUser(HttpServletRequest request) {
HttpSession session = request.getSession(false);
return session != null ? (String) session.getAttribute("currentUser") : "未登录";
}
}
@Data
class LoginRequest {
private String username;
private String password;
}
用户信息缓存实现:
java
@Service
public class UserService {
@Cacheable(value = "userDetails", key = "#username")
public UserDetails getUserDetails(String username) {
// 模拟数据库查询
System.out.println("从数据库查询用户信息: " + username);
// 实际项目中可以从数据库或其它存储中获取
return new User(username, Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
}
@CacheEvict(value = "userDetails", key = "#username")
public void clearUserCache(String username) {
System.out.println("清除用户缓存: " + username);
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{username}")
public UserDetails getUser(@PathVariable String username) {
return userService.getUserDetails(username);
}
@PostMapping("/{username}/clearCache")
public String clearCache(@PathVariable String username) {
userService.clearUserCache(username);
return "用户缓存已清除";
}
}
6、缓存注意事项
- 合理设置缓存过期时间
- 对于频繁变更的数据慎用缓存
- 考虑缓存穿透、缓存雪崩等问题
- 大对象不适合缓存
*性能优化:
- 对于热点数据可以适当延长缓存时间
- 使用本地缓存(Caffeine)作为Redis缓存的前置缓存
- 考虑使用Redis集群提高可用性和性能