SpringCache integrate Redis

What problems does SpringCache solve?

SpringCache, released in spring3.1, encapsulates and abstracts the use of caching by using annotations on methods to retrieve cached results.

With annotations, it solves the problem of coupling business code and cache code, that is, making existing code instantly cacheable without intruding on business code, and it allows developers to use caching without being aware of it.

Note: For redis cache, SpringCache only supports strings. Other Hash, List, set, and ZSet values are not supported.

Write code

Step 1: Add the POM file to the dependency package

<! --redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<! --spring cache-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<! -- Spring Cache pool dependency package -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.11.1</version>
</dependency>
Copy the code

Step 2: Add redis configuration information to the configuration file

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Redis configuration
## Redis database index (default 0)
spring.redis.database=0
# Redis server address
spring.redis.host=127.0.0.1
Redis server connection port
spring.redis.port=6379
## Redis server connection password (default null)
spring.redis.password=
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # spring cache configuration
# maximum number of connections in the pool (use negative values to indicate no limit)
spring.redis.lettuce.pool.max-active=8
Maximum connection pool blocking wait time
spring.redis.lettuce.pool.max-wait=-1ms
The maximum number of free connections in the connection pool
spring.redis.lettuce.pool.max-idle=8
Minimum free connection in connection pool
spring.redis.lettuce.pool.min-idle=0
Connection timeout (ms)
spring.redis.timeout=5000ms
The cache layer uses redis
spring.cache.type=redis
spring.cache.cache-names=redisCache
Note cache timeout: ms 10 minutes
spring.cache.redis.time-to-live=600000
Whether null values can be cached
spring.cache.redis.cache-null-values=true
# prefixes cached annotations with value as key. Default: true
spring.cache.redis.use-key-prefix=true
The # key prefix, global, invalidates the annotation prefix, but the annotation expiration time and other Settings are still in effect
spring.cache.redis.key-prefix=ljw:
Copy the code

Step 3: Enable cache configuration and set serialization

The point is to enable @enablecaching

  1. Easy configuration
/** * Spring cache configuration */
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class SpringCacheConfig {

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));

        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        // Make all the configurations in the configuration file take effect
        if(redisProperties.getTimeToLive() ! =null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if(redisProperties.getKeyPrefix() ! =null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if(! redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); }if(! redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); }returnconfig; }}Copy the code
  1. Complex Configuration

Goal: Implement multiple cache names according to the business, each cache expiration time is not the same configuration

/** * Spring cache configuration */
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class SpringCacheConfig {

    @Autowired
    private CacheProperties cacheProperties;

    /** * Cache configuration for custom annotations * 

* Configure the cache configuration when using annotations. The default is serialization and deserialization, plus this configuration is json */

@Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return new RedisCacheManager( RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), // The default policy is to use the default expiration time of 10 minutes as long as the value of the cached annotation is not defined by the map below this.getRedisCacheConfigurationWithTtl(cacheProperties.getRedis().getTimeToLive()), // @cacheable (value = "redisExpire1h",key = "'a'+#itemCode"); More than one can be customized this.getRedisCacheConfigurationMap() ); } / * * * annotations on the value of the corresponding is the configuration in the cacheManager redisCacheConfigurationMap (map can put more configuration), * specified here is redisExpire1h; * No write or no match is used. The default cacheManager defaultCacheConfig * * is used@return* / private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(a) { Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(); // Duration 1 hour redisCacheConfigurationMap.put("redisExpire1h".this.getRedisCacheConfigurationWithTtl(Duration.ofMillis(3600000L))); // The expiration time is one day redisCacheConfigurationMap.put("redisExpire1d".this.getRedisCacheConfigurationWithTtl(Duration.ofMillis(86400000L))); return redisCacheConfigurationMap; } /** * Configures the configuration information of different cache areas. * * can be distinguished according to business@param duration * @return* / private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Duration duration) { // Get the default cache configuration RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() // Set the expiration time of each cache .entryTtl(duration) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); CacheProperties.Redis redisProperties = cacheProperties.getRedis(); // Map all the configurations in the configuration file to the CacheProperties class. If you want them to take effect, you need to reassign config /* if (redisProperties.getKeyPrefix() ! = null) {// Default specifies the prefix of the cacheNames key: @cacheconfig (cacheNames = {"user"}) and @cacheable (cacheNames = "redisExpire1h", key = "'checkTTL1Hour'") are prefixed with user:: And redisExpire1h: : / / add custom prefix, if here or application configuration file. The properties specified prefix is cover annotation prefix config = config prefixKeysWith (redisProperties. GetKeyPrefix ()); } * / if(! redisProperties.isCacheNullValues()) {// indicates null values are not allowed to be cached config = config.disableCachingNullValues(); } if(! redisProperties.isUseKeyPrefix()) {// Do not use the default prefix config = config.disableKeyPrefix(); } returnconfig; }}Copy the code

Step 4: Logical code

The controller

@api (description = "SpringCache ")
@RestController
@RequestMapping("/userSpringCache")
public class UserSpringCacheController {

    @Autowired
    private UserSpringCacheService userSpringCacheService;


    @apiOperation (" Single user query, query user information by userID ")
    @RequestMapping(value = "/findById/{id}", method = RequestMethod.GET)
    public UserVO findById(@PathVariable int id) {
        User user = this.userSpringCacheService.findUserById(id);
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @apiOperation (" Modify data ")
    @PostMapping(value = "/updateUser")
    public void updateUser(@RequestBody UserVO obj) {
        User user = new User();
        BeanUtils.copyProperties(obj, user);
        userSpringCacheService.updateUser(user);
    }

    @apiOperation (" delete user by ID ")
    @RequestMapping(value = "/del/{id}", method = RequestMethod.GET)
    public void deleteUser(@PathVariable int id) {
        this.userSpringCacheService.deleteUser(id);
    }


    @apiOperation (" Check whether the cache time is 1 hour ")
    @RequestMapping(value = "/checkTTL1Hour", method = RequestMethod.GET)
    public String checkTTL1Hour(a) {
        String ttl = this.userSpringCacheService.checkTTL1Hour();
        return ttl;
    }

    @apiOperation (" Check whether the cache time is 1 day ")
    @RequestMapping(value = "/checkTTL1Day", method = RequestMethod.GET)
    public String checkTTL1Day(a) {
        String ttl = this.userSpringCacheService.checkTTL1Day();
        return ttl;
    }


    @ ApiOperation (" default TTL ")
    @RequestMapping(value = "/deafultTTL", method = RequestMethod.GET)
    public String deafultTTL(a) {
        String ttl = this.userSpringCacheService.deafultTTL();
        return ttl;
    }
    
    @ ApiOperation (caching the use of ")
    @RequestMapping(value = "/caching", method = RequestMethod.GET)
    public String caching(a) {
        String ttl = this.userSpringCacheService.caching();
        returnttl; }}Copy the code

Service class

@Service
@CacheConfig(cacheNames = {"user"})
@Slf4j
public class UserSpringCacheServiceImpl extends ServiceImpl<UserMapper.User> implements UserSpringCacheService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    /** * The default time is 10 minutes. Key is the prefix configured in the configuration file + ID * or@CacheConfig(cacheNames = {"user"}) Configured user:: ID * *@param id
     * @return* /
    @Override
    @Cacheable(key = "#id")
    public User findUserById(Integer id) {
        return this.userMapper.selectById(id);
    }

    /** * Update will change the TTL to 10 minutes **@return* /
    @Override
    @CachePut(key = "#obj.id")
    public User updateUser(User obj) {
        this.userMapper.updateById(obj);
        return this.userMapper.selectById(obj.getId());
    }

    @Override
    @CacheEvict(key = "#id")
    public void deleteUser(Integer id) {
        User user = new User();
        user.setId(id);
        user.setDeleted((byte) 1);
        this.userMapper.updateById(user);
    }

    / * * * expiration time use redisExpire1h cache the expiration time * key - > redisExpire1h: : checkTTL1Hour * *@return* /
    @Override
    @Cacheable(cacheNames = "redisExpire1h", key = "'checkTTL1Hour'")
    public String checkTTL1Hour(a) {
        Long expire = redisTemplate.getExpire("checkTTL1Hour");
        log.info("checkTTL1Hour:" + expire);
        return String.valueOf(expire);
    }

    / * * * expiration time use redisExpire1d cache the expiration time * key - > redisExpire1d: : checkTTL1Day * *@return* /
    @Override
    @Cacheable(cacheNames = "redisExpire1d", key = "'checkTTL1Day'")
    public String checkTTL1Day(a) {
        Long expire = redisTemplate.getExpire("checkTTL1Day");
        log.info("checkTTL1Hour:" + expire);
        return String.valueOf(expire);
    }

    /** * This cache name is not configured with noExistName, default TTL * > noExistName::deafultTTL specify noExistName as key **@return* /
    @Override
    @Cacheable(cacheNames = "noExistName", key = "'deafultTTL'")
    public String deafultTTL(a) {
        Long expire = redisTemplate.getExpire("deafultTTL");
        log.info("checkTTL1Hour:" + expire);
        return String.valueOf(expire);
    }
    
    / * * *@CachingAnnotations allow us to specify multiple Spring Cache-related annotations on a method or class at the same time. * It has three attributes: cacheable, PUT, and EVict, which are used to specify each@Cacheable,@CachePutand@CacheEvict. * /
    @Override
    @Caching( cacheable = { @Cacheable(cacheNames = "redisExpire1h", key = "'1002'"), @Cacheable(cacheNames = "redisExpire1d", key = "'1002'") }, evict = { @CacheEvict(value = "CacheEvict1", key = "'1002'"), @CacheEvict(value = "CacheEvict2", key = "'1002'") })
    public String caching(a) {
        redisTemplate.opsForValue().set("CacheEvict1::1002"."Caching");
        redisTemplate.opsForValue().set("CacheEvict2::1002"."Caching");
        return "Caching"; }}Copy the code

Parsing common annotations in SpringCache

@CacheConfig

@Cacheconfig is a class-level annotation that unifies the prefix of all cache keys for the class and the application’s cache name configuration, such as TTL. If cacheNames does not exist, this is the default

@CacheConfig(cacheNames = { "user" })
public class UserSpringCacheServiceImpl extends ServiceImpl<UserMapper.User> implements UserSpringCacheService {
Copy the code

The code above shows that all caches of this class can be prefixed with “user::”

@Cacheable

@cacheable is a method-level annotation used to cache the results of a method.

@Cacheable(key="#id")
public User findUserById(Integer id){
    return this.userMapper.selectById(id);
}

Copy the code

The above method is called to read data from the cache, execute the method body if the cache does not find the data, and finally add the return value to the cache.

Note:

@cacheable is typically used in conjunction with @Cacheconfig

For example, @cacheconfig (cacheNames = {“user”}) is used in conjunction with @cacheable (key=”#id”).

The key for the user: : # id

@Cacheable(cacheNames = "redisExpire1d", key = "'checkTTL1Day'")
Copy the code

RedisExpire1d cache configuration, they have a custom expiration time. The key for redisExpire1d: : checkTTL1Day

@CachePut

@cachePUT is a method-level annotation used to update the cache.

@CachePut(key = "#obj.id")
public User updateUser(User obj){
    this.userMapper.updateById(obj);
    return this.userMapper.selectById(obj.getId());
}
Copy the code

When the above method is called, the method body is executed, and springCache updates the cache with the return value key = “#obj.id”, value=User

@CacheEvict(key = “#id”)

@cachePUT is a method-level annotation to remove the cache.

@CacheEvict(key = "#id")
public void deleteUser(Integer id){
    User user=new User();
    user.setId(id);
    user.setDeleted((byte)1);
    this.userMapper.updateById(user);
}
Copy the code

When the above method is called, the method body is executed and the cache is removed by method arguments

@Caching

@caching is a method-level annotation for multiple operations

@Caching( cacheable = { @Cacheable(cacheNames = "redisExpire1h", key = "'1002'"), @Cacheable(cacheNames = "redisExpire1d", key = "'1002'") }, evict = { @CacheEvict(value = "CacheEvict1", key = "'1002'"), @CacheEvict(value = "CacheEvict2", key = "'1002'") })
Copy the code

The above method is called with two operations cached and two operations deleted.

Disadvantages of SpringCache

  1. For redis cache, springCache only supports String. Other Hash, List, set, and ZSet are not supported. Therefore, RedisTemplate can only be used for Hash, List, set, and ZSet

  2. The return value of the method is cached. If a method requires multiple complex business caches, only RedisTemplate can be used.

Download the source code

The source code

Redis distributed cache family

  • Redis Distributed Cache (1) – Redis Installation (Linux and Docker)
  • Redis Distributed cache (ii) – RDB and AOF
  • SpringBoot integrates Mybatis-Plus,Redis and Swagger
  • The article continues to be updated…