9、缓存与Session共享

缓存是一种提高系统性能的常用技术,它将频繁访问的数据存储在内存中,减少数据库访问次数。


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集群提高可用性和性能
相关推荐
全栈弄潮儿²⁰²⁴1 天前
AI Agent 开发实战(30):限流、缓存与成本控制
人工智能·gpt·缓存·agent·限流·agi·成本控制
Together_CZ2 天前
DeepSeek-V4.1-Flash:Pushing the Limits of KV Cache Compression——推动 KV 缓存压缩的极限
缓存·llm·compression·kv cache·deepseek·v4.1-flash·推动 kv 缓存压缩的极限
Pioneer000012 天前
我用 Redis + 网关做多模型 API 路由:缓存命中率 95%+ 的工程实践
人工智能·redis·后端·缓存·性能优化·架构
hweiyu003 天前
Redis命令:MSETNX
redis·缓存
wdfk_prog3 天前
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输
运维·缓存·docker·容器·ros
是Dream呀3 天前
Harness 工程:让 Agent 真正把任务做完
人工智能·分布式·缓存·agent
努力努力再努力wz3 天前
【Redis进阶系列】:从主从复制到 Sentinel,一文建立故障检测、Leader 选举与 Failover 的完整心智模型
数据库·redis·缓存
wdfk_prog4 天前
ROS教程10:从 gtest 到 rostest——Unit Test、Node 集成测试、rosbag 与 rqt 验证闭环
运维·缓存·docker·容器·ros
程序猿乐锅4 天前
【黑马点评 | 第四篇】Redis缓存雪崩
java·数据库·spring boot·redis·缓存
HanhahnaH5 天前
Redis单线程和Tair多线程架构设计对比
分布式·缓存