Spring cache是代码级的缓存,他一般是使用一个ConcurrentMap。也就是说实际上还是是使用JVM的内存来缓存对象的,那么肯定会造成大量的内存消耗。但是使用方便。

Redis 作为一个缓存服务器,是内存级的缓存。它是使用单纯的内存来进行缓存。

那么Spring cache +redis的好处显而易见了。既可以很方便的缓存对象,同时用来缓存的内存的是使用redis的内存,不会消耗JVM的内存,提升了性能。

回归正题:

Redis的安装:

1. 需要引用的依赖包(gradle):

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-json'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
    implementation 'org.slf4j:slf4j-migrator:1.8.0-beta2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

2. 配置:

# redis
spring.redis.host=192.168.5.129
spring.redis.port=6379

# pool settings:
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1

## Whether to use the key prefix when writing to Redis.
spring.cache.redis.use-key-prefix=true

3. 配置Spring Cache管理Redis。

这里使用的是redis提供的序列化方式,有些类型在序列化时是不支持的):

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    private static final Logger log = LoggerFactory.getLogger(RedisConfig.class);

    /**
     * 自定义生成redis-key
     *
     * @return
     */
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName()).append(".");
                sb.append(method.getName()).append(".");
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                log.info("------> 自定义生成redis-key完成,keyGenerator=" + sb.toString());
                return sb.toString();
            }
        };
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)) // 设置缓存有效期一小时
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                .disableCachingNullValues();

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();

        log.info("------> 自定义RedisCacheManager加载完成");
        return redisCacheManager;
    }


    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());

        log.info("------> 自定义RedisTemplate加载完成");
        return redisTemplate;
    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer<Object> valueSerializer() {
        return new Jackson2JsonRedisSerializer(Object.class);
    }

使用@JsonComponent注解自定义序列化方式):

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    private static final Logger log = LoggerFactory.getLogger(RedisConfig.class);

    /**
     * 自定义生成redis-key
     *
     * @return
     */
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName()).append(".");
                sb.append(method.getName()).append(".");
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                log.info("------> 自定义生成redis-key完成,keyGenerator=" + sb.toString());
                return sb.toString();
            }
        };
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
                .transactionAware()
                .build();

        log.info("------> 自定义RedisCacheManager加载完成");
        return redisCacheManager;
    }

}

使用@JsonComponent注解 自定义User实体类Json格式序列化, spring boot框架在处理封装User实体类时自己会调用此序列化方法。

@JsonComponent
public class UserJsonSerializer extends JsonSerializer<User> {
    @Override
    public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("id",user.getId().toString());
        jsonGenerator.writeStringField("username",user.getUsername());
        jsonGenerator.writeStringField("password",user.getPassword());
        jsonGenerator.writeEndObject();
    }
}

注:该实体类必须实现Serializable 接口

@Document("t_user")
public class User implements Serializable {
    @Id
    private ObjectId id;

    private String username;

    private String password;

    public ObjectId getId() {
        return id;
    }

    public void setId(ObjectId id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

4. Spring Cache + Redis 的使用。

@Service
@CacheConfig
public class RedisServiceTest {

    private MongoOperations mongoOperations;

    public RedisServiceTest(MongoOperations mongoOperations) {
        this.mongoOperations = mongoOperations;
    }

    /**
     * 注解@Cacheable中存在有以下几个元素
     *
     * value (也可使用 cacheNames) : 可看做命名空间,表示存到哪个缓存里了。
     * key : 表示命名空间下缓存唯一key,使用Spring Expression Language(简称SpEL,详见参考文献[5])生成。
     * condition : 表示在哪种情况下才缓存结果(对应的还有unless,哪种情况不缓存),同样使用SpEL
     * @return
     */
    @Cacheable(cacheNames="user",key = "'userAll'")
    public List<User> getUserAll(){
        long oneNow = System.currentTimeMillis();
        List<User> all = mongoOperations.findAll(User.class);
        System.out.println("耗时:" + (System.currentTimeMillis() - oneNow) + "ms");
        return all;
    }

    /**
     * 注解 @CacheEvict 中存在有以下几个元素 
     * - value (也可使用 cacheNames) : 同Cacheable注解,可看做命名空间。表示删除哪个命名空间中的缓存 
     * - allEntries: 标记是否删除命名空间下所有缓存,默认为false 
     * - key: 同Cacheable注解,代表需要删除的命名空间下唯一的缓存key。
     */
    @CacheEvict(cacheNames="user",key = "'userAll'")
    public void deleteRedis(){

    }

}

 

开发中使用需要在Spring boot启动类开启缓存

@SpringBootApplication
@EnableCaching//开启缓存
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}