SpringBoot2.1版本的我的应用开发框架 - 集成Redis缓存

本篇做为SpringBoot2.1版本的我的开发框架 子章节,请先阅读SpringBoot2.1版本的我的开发框架再次阅读本篇文章html

项目地址:SpringBoot2.1版本的我的应用开发框架前端

前端项目地址:ywh-vue-adminvue

关于为何要用Redis?

项目中为何要用Redis缓存?其实在我实习时是用到过Redis缓存的,可是我只是知道用到了Redis,如何使用的,为何要用,这些我通通都不知道。引用网上一句话就是,我此次集成也是为了Redis而Redis了,并非拿Redis来解决实际的问题,可是我以为只有先学会了,才能知道在什么状况下能够用Redis来解决问题。java

为何要用redis?引用网上的言论git

1.解决应用服务器的cpu和内存压力 2.在项目中使用 Redis,主要考虑两个角度:性能和并发。 3.咱们在碰到须要执行耗时特别久,且结果不频繁变更的 SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求可以迅速响应。 4.在大并发的状况下,全部的请求直接访问数据库,数据库会出现链接异常。这个时候,就须要使用redis作一个缓冲操做,让请求先访问到redis,而不是直接访问数据库。 5.排行榜及相关问题。排行榜(leader board)按照得分进行排序。zadd命令能够直接实现这个功能,而zrevrange命令能够用来按照得分来获取前100名的用户,zrank能够用来获取用户排名,很是直接并且操做容易。 6.计数的问题,好比点赞和转发数,经过原子递增保持计数;getset用来重置计数器;过时属性用来确认一个关键字何时应该删除。github

个人学习笔记:redis

并且Redis能够master-slave(主从)模式进行数据备份,在分布式的系统中,能够很好保证数据的备份,Redis会自动把主数据库(master)中的数据备份到从数据库(slave)中,关于为何要用Redis这件事情上,除了自己本身项目中的缘由;剩下的有不少缘由均可以事先在网络上汲取知识以及经验。json

SpringBoot的缓存支持

咱们把缓存的事情放到ywh-starter-cache这个模块中来集成,在cache中的pom.xml文件中引入spring-boot-starter-data-redis依赖

<!-- redis依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>
复制代码

cache项目结构分为

  • config - 放配置类
  • serializer - 放序列化类
  • utils - 放工具类

源码级分析

为何用户须要本身建立一个redis的配置类呢?

SpringBoot提供了对Redis的自动配置功能,在RedisAutoConfiguration类中默认为咱们配置了客户端链接(Lettuce和Jedis),以及数据操做模板(StringRedisTemplate和RedisTemplate),下列代码有一个@ConditionalOnMissingBean和@Bean的注解,@ConditionalOnMissingBean注解判断是否执行初始化代码,即若是用户已经建立了bean,则相关的初始化代码再也不执行。这致使了默认的是redisTemplate方法会被执行。

public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}
复制代码

RedisTemplate这个数据操做模板类咱们能够点击去看一看,在类中有一段代码

if (this.defaultSerializer == null) {
    this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
复制代码

若是默认的序列化为空则使用jdk来序列化咱们的数据,而 defaultSerializer 这个私有属性,默认为NULL,因此默认的序列方式时jdk的方式,可是这个序列化方式会把数据变得人看不懂,因此才须要建立一个Redis配置类覆盖默认的序列化,若是我有分析的有不对的地方,望指正。

简单的链接

分析事后,就以两种方式来进行链接。第一种:不更改默认配置使用StringRedisTemplate和RedisTemplate

在咱们写代码测试以前须要在cache模块中配置application-redis.yml文件,而且把core下的application.yml文件中的active属性添加redis 以逗号相隔这样就可在运行的时候读取application-redis.yml的内容,把cache模块下application.properties修改为application-redis.yml文件进行配置。

spring:
 redis:
    # Redis数据库索引(默认为0)
 database: 0
    # Redis服务器地址
 host: 127.0.0.1
    # Redis服务器链接端口
 port: 6379
    # Redis服务器链接密码(默认为空)若是没有配置密码就不要写这个属性了
 password: 123456
    #链接池
 lettuce:
 pool:
        #链接池最大链接数(使用负值表示没有限制)
 max-active: 8
        #链接池最大阻塞等待时间(使用负值表示没有限制)
 max-wait: 60000
        #链接池中的最大空闲链接
 max-idle: 8
        #链接池中的最小空闲链接
 min-idle: 0
    #链接超时时间(毫秒)
 timeout: 10000
复制代码

在SpringBoot测试类中编写代码并运行添加数据。

@Autowired
private StringRedisTemplate stringRedisTemplate;

/** * 测试链接redis,并存入数据 */
@Test
public void redisTest(){
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    stringRedisTemplate.opsForValue().set("abc","测试");
    stringRedisTemplate.opsForList().leftPushAll("ywh",list);

}
复制代码

StringRedis测试数据

咱们在用StringRedisTemplate添加的数据显示正常,也是咱们人眼能读懂的,接下来咱们用RedisTemplate这个类来链接Redis添加数据看一看数据是什么样的,默认的序列化JdkSerializationRedisSerializer的二进制数据序列化方式,代码跟上面差很少。

@Autowired
private RedisTemplate<String,Object> redisTemplate;

@Test
public void redisTest1(){
    List<String> list = new ArrayList<>();
    list.add("y");
    list.add("w");
    list.add("h");
    redisTemplate.opsForValue().set("redisTemplate","链接成功了");
    redisTemplate.opsForList().leftPushAll("redis",list);
}
复制代码

RedisTemplate测试数据

果真添加的数据咱们人眼分辨不出来这是什么,因此下面咱们要进行覆盖默认的配置,定制本身的序列化方式。

覆盖默认配置进行链接

spring为咱们提供了多种序列化方式,都在org.springframework.data.redis.serializer包下,经常使用的分别是:

  • JdkSerializationRedisSerializer
  • GenericJackson2JsonRedisSerializer
  • StringRedisSerializer
  • Jackson2JsonRedisSerializer

这四种咱们只使用StringRedisSerializer来序列化Key值,value值由咱们本身建立的序列化类,serializer包下建立咱们自定义的FastJsonRedisSerializer类,须要实现RedisSerializer接口,实现接口中的序列化方法和反序列化方法,使用的是alibaba的Fastjson实现。

package com.ywh.cache.serializer;

/** * CreateTime: 2018-12-19 16:51 * ClassName: FastJsonRedisSerializer * Package: com.ywh.cache.serializer * Describe: * 自定义的序列化类 * @author YWH */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
    private ObjectMapper objectMapper = new ObjectMapper();
 
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
 
    private Class<T> clazz;
 
    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }
 
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }
 
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);
 
        return JSON.parseObject(str, clazz);
    }
    public void setObjectMapper(ObjectMapper objectMapper) {
        Assert.notNull(objectMapper, "'objectMapper' must not be null");
        this.objectMapper = objectMapper;
    }
 
    protected JavaType getJavaType(Class<?> clazz) {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}
复制代码

建立好自定义的序列化类后,进行覆盖默认的配置,接下来在config包下建立RedisCacheConfig类,配置好Redis配置类之后咱们再次从新运行测试类,会发现值再也不是乱码了。

package com.ywh.cache.config;
 
/** * CreateTime: 2018-12-18 23:34 * ClassName: RedisCacheConfig * Package: com.ywh.cache.config * Describe: * Redis缓存配置 @EnableCaching 开启声明式缓存支持. 以后就可使用 @Cacheable/@CachePut/@CacheEvict 注解缓存数据. * @author YWH */
@Configuration
@EnableCaching
public class RedisCacheConfig {
 
 
    private RedisConnectionFactory redisConnectionFactory;
 
    @Autowired
    public void setRedisConnectionFactory(RedisConnectionFactory redisConnectionFactory){
        this.redisConnectionFactory = redisConnectionFactory;
    }
 
    /** * 覆盖默认的配置 * @return RedisTemplate */
    @Bean
    public RedisTemplate<String,Object> redisTemplate(){
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
 
 
        // 设置value的序列化规则和key的序列化规则
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        template.setDefaultSerializer(fastJsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
 
    /** * 解决注解方式存放到redis中的值是乱码的状况 * @param factory 链接工厂 * @return CacheManager */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
 
        // 配置注解方式的序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration redisCacheConfiguration =
                config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
                        //配置注解默认的过时时间
                        .entryTtl(Duration.ofDays(1));
        // 加入白名单 https://github.com/alibaba/fastjson/wiki/enable_autotype
        ParserConfig.getGlobalInstance().addAccept("com.ywh");
        ParserConfig.getGlobalInstance().addAccept("com.baomidou");
        return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
    }
}
复制代码

Redis工具类

redis能够对五种类型操做,能够本身再进行扩展,工具类咱们放在cache模块下建立utils包,建立RedisUtil工具类,类上添加@Component注解,交给Spring来管理,咱们能够直接在其余的方法中用@AtuoWired获取。

package com.ywh.cache.utils;
/** * CreateTime: 2018-12-19 17:45 * ClassName: RedisUtil * Package: com.ywh.cache.utils * Describe: * Redis工具类 * * @author YWH */
@Component
public class RedisUtil {
 
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
 
    //---------------------- common --------------------------
 
    /** * 指定缓存失效时间 * @param key key值 * @param time 缓存时间 * @return true设置成功,time <=0 设置失败 */
    public void expire(String key, long time){
        if(time > 0){
            redisTemplate.expire(key,time,TimeUnit.SECONDS);
        }else{
            throw MyExceptionUtil.mxe("设置的时间不能为0或者小于0!!");
        }
    }
 
    /** * 判断key是否存在 * @param key * @return true 存在 false 不存在 */
    public Boolean existsKey(String key){
        return redisTemplate.hasKey(key);
    }
 
    。。。。省略代码,具体代码可前往github查看
 
}
复制代码
相关文章
相关标签/搜索