自学内容网 自学内容网

SpringCache的入门使用以及在项目里的案例

今天在项目中用到了SpringCache,也首次面试会问的各种缓存问题,话不多说进行正题。

几种基本注解的使用

1. @Cacheable

@Cacheable 是 Spring Cache 中最常用的注解,用于将方法的返回结果缓存起来,以后再调用该方法时,如果缓存中已有结果,就不执行方法,而是直接返回缓存中的结果。

用法

@Cacheable(value = "users", key = "#userId")
public User getUserById(Long userId) {
    // 查询数据库
    return userRepository.findById(userId).orElse(null);
}
  • value:指定缓存的名称,比如 "users"
  • key:指定缓存的 key,默认使用方法参数。如果不指定,Spring Cache 会自动使用方法参数作为 key。

常见属性

  • condition:可以设置条件,只有满足条件时才缓存。
  • unless:可以设置条件,满足条件时不缓存。
  • sync:设置为 true,可以防止缓存击穿,确保同一时刻只有一个线程能查询数据库并将结果缓存。

示例

@Cacheable(value = "users", key = "#userId", condition = "#userId > 0", unless = "#result == null")
public User getUserById(Long userId) {
    return userRepository.findById(userId).orElse(null);
}

2. @CachePut

@CachePut 表示不管缓存中是否有值,都会执行方法并将结果放入缓存。常用于更新缓存。

用法

@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
    // 更新数据库
    return userRepository.save(user);
}
  • value:缓存的名称。
  • key:指定缓存的 key。

@CachePut 每次都会执行方法,并将返回值放入缓存中,用于保证缓存与数据库数据的一致性。

3. @CacheEvict

@CacheEvict 用于从缓存中移除数据,常用于删除或更新数据时同步清除缓存。

用法

@CacheEvict(value = "users", key = "#userId")
public void deleteUser(Long userId) {
    // 删除数据库中的用户
    userRepository.deleteById(userId);
}
  • value:缓存的名称。
  • key:要移除的缓存的 key。

常见属性

  • allEntries = true:删除该缓存名称下的所有缓存。
  • beforeInvocation = true:在方法执行前清除缓存,默认是 false,即方法执行后再清除缓存。

示例

@CacheEvict(value = "users", allEntries = true)
public void clearAllUsersCache() {
    // 清除所有缓存
}

4. @Caching

@Caching 可以同时组合使用多个缓存相关的注解,适用于复杂的缓存操作。

用法

@Caching(
    put = { @CachePut(value = "users", key = "#user.id") },
    evict = { @CacheEvict(value = "cacheName", key = "#user.id") }
)
public User saveOrUpdateUser(User user) {
    return userRepository.save(user);
}

通过 @Caching,你可以同时定义多个 @Cacheable@CachePut@CacheEvict 注解,完成复杂的缓存逻辑。

5. @CacheConfig

@CacheConfig 用于类级别的注解,统一配置缓存的名称和其它属性,减少重复配置。

用法

@CacheConfig(cacheNames = "users")
public class UserService {
    
    @Cacheable(key = "#userId")
    public User getUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }

    @CachePut(key = "#user.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }
}
  • cacheNames:指定默认的缓存名称,类中所有的缓存注解会默认使用这个名称。

小结:

  • @Cacheable:将方法的返回结果缓存。
  • @CachePut:更新缓存,即使缓存中有数据,也会重新执行方法并更新缓存。
  • @CacheEvict:从缓存中移除数据。
  • @Caching:组合多个缓存注解。
  • @CacheConfig:类级别统一配置缓存名称等属性。


配置

要使用SpringCache需要首先开启配置:

1. 引入依赖

首先,在你的 pom.xml 中添加 Spring Cache 相关的依赖,如果你使用的是基于内存的缓存(如 ConcurrentMapCache),可以使用如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

如果你打算使用 Redis、EhCache 等其他缓存实现,还需要引入对应的依赖,例如 Redis 缓存:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. 开启缓存支持

在你的主类Application或任意配置类上加上 @EnableCaching 注解:

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {
    // 你可以在这里配置缓存相关的 Bean 或者使用默认配置
}
@SpringBootApplication
@EnableScheduling
@EnableCaching
public class MyAppApplication {
//也可以在主类上面加
    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }

}

3. 配置缓存(可选)

Spring Boot 默认使用 ConcurrentMapCache,即内存中的缓存。如果需要使用其他缓存实现,你可以在 application.properties 中进行配置。比如使用 Redis 缓存:

  # Redis
  data:
    redis:
      port: 6379
      host: ${your_host}
      password: ${pwd_if_needed}

  # Cache
  cache:
    type: redis # 使用redis作为cache

4. 使用缓存注解

一旦启用缓存支持后,你就可以使用 @Cacheable, @CachePut, @CacheEvict 等注解进行缓存操作。

小结:

  • 引入依赖:添加 spring-boot-starter-cache 依赖。
  • 开启缓存:在配置类上加 @EnableCaching
  • 配置缓存类型:在 application.properties 中配置缓存类型(如 Redis 或默认内存缓存)。
  • 使用注解:在服务方法上使用 @Cacheable, @CachePut, @CacheEvict 等缓存注解。

这就完成了 Spring Cache 的基本配置和开启。





现在再来说说在最近实际项目中的应用

JwtFilter:

在用户登陆的时候根据Token里解析出来的id去查询数据库,将查询的结果进行缓存,最终返回一个LoginUser对象给SringSecurity认证。

  /**
     * parse the token and get the user information to create a LoginUser object
     * @param token token to parse
     * @return LoginUser object
     * @throws UsernameNotFoundException if the user is not found
     * @Description: get the newest user information from the database if the user is not in the cache!
     */
    private LoginUser getLoginUser(String token) {
        // Parse the token to get the user information
        Map<String, Object> claims = jwtUtil.parseToken(token);

        // get the user information from the database can make sure the user is up to date
        Long userId = Long.parseLong(claims.get("userId").toString());
        // if the user is not in the cache, get the user from the database
        User user = cachedUserService.getUserById(userId);
        if (user != null) {
            user.setPassword(null);// 不将密码传递保存在redis中
        }else {
            throw new UsernameNotFoundException("User not found");
        }
        List<String> roles = cachedUserService.getRolesByUserId(userId);
        List<String> permissions = cachedUserService.getPermissionsByUserId(userId);

        return new LoginUser(user, permissions, roles);
    }

这里cachedUserService就是专门用来在经过jwtfilter的是手查询用户的最新的信息的,减少数据库的负担。


/**
 * @Author: Yupeng Li
 * @Date: 22/9/2024 02:58
 * @Description: This class is used to cache user, role and permission data in Redis cache.
 * when a new API request comes in, it will go through the JWT filter,
 * and then the CachedUserService will be called to get the user, role and permission data from the cache.
 * <p>
 * If the data is not in the cache, it will be fetched from the database and then stored in the cache.
 * If the data is already in the cache, it will be fetched from the cache directly.
 * <p>
 * when the user, role and permission data is updated, the cache will be evicted.
 * <p>
 * The aim of this class is to reduce the number of database queries and improve the performance of the application.
 */
@Service
public class CachedUserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private PermissionMapper permissionMapper;

    private static final Logger logger = LoggerFactory.getLogger(CachedUserService.class);

    @Cacheable(value = "users", key = "#userId")
    public User getUserById(Long userId) {
        return userMapper.finUserById(userId);
    }

    @Cacheable(value = "roles", key = "#userId")
    public List<String> getRolesByUserId(Long userId) {
        return roleMapper.findRolesByUserId(userId);
    }

    @Cacheable(value = "permissions", key = "#userId")
    public List<String> getPermissionsByUserId(Long userId) {
        return permissionMapper.findPermissionsByUserId(userId);
    }

    @CacheEvict(value = "users", key = "#userId")
    public void evictUserCache(Long userId) {
        logger.info("User cache evicted: {}", userId);
    }

    @CacheEvict(value = "roles", key = "#userId")
    public void evictRolesCache(Long userId) {
        logger.info("Roles cache evicted: {}", userId);
    }

    @CacheEvict(value = "permissions", key = "#userId")
    public void evictPermissionsCache(Long userId) {
        logger.info("Permissions cache evicted: {}", userId);
    }
}

这里有一个坑要提别的提醒大家,就是在这个CachedUserService里面你不能直接在这个类里面去调用其他方法。比如你这个类里面有个evictAllCahe()这个方法,你想在这个方法里面直接调用其他的三个方法evictRolesCache()evictUserCache()evictPermissionsCache()很负责任的告诉你,当你在其他类里面调用evictAllCahe()企图删除所有缓存时是不会生效的。方法可以执行,但是缓存不会被删除,别问我怎么知道的(被坑惨了哈哈哈。


主要原因好像是Spring的AOP机制什么什么的,没了解太深。反转正确的做法就是你吧evictAllCahe()这个方法写到其他类里面去然后再调用就可以了,同一个类里面就不行!

public class LoginServiceImpl {

   protected ResponseEntity<Result> completeProfileUpdate(User user) {
      ......

        // 每当用户更新profile,移除所有的用户缓存,以便下次查询时重新加载数据!
        evictAllUserCaches(user.getId());

......

        return ResponseEntity.ok(Result.success("Profile updated successfully"));
    }

 /**
     * 移除所有用户缓存
     * @param userId 用户ID
     */
    private void evictAllUserCaches(Long userId) {
        cachedUserService.evictUserCache(userId);
        cachedUserService.evictRolesCache(userId);
        cachedUserService.evictPermissionsCache(userId);
    }
  }

原文地址:https://blog.csdn.net/weixin_55592317/article/details/142441878

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!