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集群提高可用性和性能
相关推荐
典孝赢麻崩乐急9 小时前
Redis复习------跳表
数据库·redis·缓存
✿ ༺ ོIT技术༻9 小时前
Redis:Redis背景、特性、客户端及单线程模型
数据库·redis·缓存
程序员阿鹏9 小时前
如何保证写入Redis的数据不重复
java·开发语言·数据结构·数据库·redis·缓存
斯普信专业组10 小时前
Redis Cluster 集群化部署全流程指南:从源码编译到容器化
数据库·redis·缓存
Swift社区11 小时前
LeetCode 460 - LFU 缓存
算法·leetcode·缓存
全栈工程师修炼指南12 小时前
Nginx | ngx_cache_purge 模块:实现清除特定上游服务(后端)响应缓存条目
运维·nginx·缓存
程序员阿鹏14 小时前
RabbitMQ持久化到磁盘中有个节点断掉了怎么办?
java·开发语言·分布式·后端·spring·缓存·rabbitmq
panzer_maus14 小时前
Redis的简单介绍(1)
数据库·redis·缓存
WitsMakeMen14 小时前
训练时开启 KV 缓存会和is_causal=False 冲突
人工智能·缓存·语言模型·自然语言处理·llm·transformer
黎雁·泠崖14 小时前
C 语言文件操作高阶:读取结束判定 + 缓冲区原理 + 常见错误
c语言·开发语言·缓存