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集群提高可用性和性能
相关推荐
qq_316837753 小时前
uniapp 缓存请求文件时 判断是否有文件缓存 并下载和使用
前端·缓存·uni-app
跟着珅聪学java4 小时前
Jedis SetParams教程:参数化设置 Redis 键值对
数据库·redis·缓存
赖small强5 小时前
【Linux 内存管理】深入解析 Linux Cache Line 的原理、价值及 MIPS CPU 处理机制
linux·缓存·内存对齐·cache line
卿雪5 小时前
Redis 数据过期删除和内存淘汰策略
数据库·redis·缓存
C++业余爱好者5 小时前
Springboot中的缓存使用
spring boot·后端·缓存
武子康6 小时前
Java-185 Guava Cache 实战:删除策略、过期机制与常见坑全梳理
java·spring boot·redis·spring·缓存·guava·guava cache
only-qi17 小时前
Redis如何应对 Redis 大 Key 问题
数据库·redis·缓存
天生励志12320 小时前
Redis 安装部署
数据库·redis·缓存
武帝为此1 天前
【Redis 数据库介绍】
数据库·redis·缓存