乐趣区

关于java:第四阶段笔记-Jingtaoday11

day 11 Redis

此文档是依据上课流程编写,更多细节及图片请参见刘老师的专栏

江哥的专栏

《cgb2008- 京淘 day11》

一. Redis 客户端命令
  1. Hash 类型

    i. 阐明

    ​ 能够用散列类型保留 ” 对象 ” 和属性值,个别在工作中存储的都是基于一个业务对象的数据。

    ​ user{“name”:”aa”,”age”:5}

    ii. Hash 类型命令

    hset user name aa
    hset user age 5
    hget user age => "5"
    hgetall user => "name" "aa" "age" "5"
    hkeys user => "name" "age"
  2. List 类型

    i. 阐明

    ​ 双端循环链表,能够从首尾两端拜访数据 L===R

    ii. List 类型命令

    队列:lpush list 1 2 3 4 5 rpop ==> 1
    栈:lpush list 1 2 3 4 5 lpop ==> 5
  3. Set 类型

    sadd set1 a
    sadd set1 b
    scard set1
    sinter set1 set2
    sinterstore set3 set1 set2
    sismember set1 c
    smembers set1
  4. 事务命令

    Redis 中的事务是弱事务,如果在分布式系统中无奈对事务进行管制。

    multi
    exec
    discard
二. 应用程序操作 redis
  1. 整合入门案例

    i. 导入 jar 包

    <!--spring 整合 redis -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
    </dependency>

    ii. 创立 TestRedis 测试类

    public class TestRedis {
        @Test
        // 连贯服务:IP 地址: 端口
        public void testSetGet() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379);
            jedis.flushAll(); // 先清空 redis 缓存
            //1. 存取 redis
            jedis.set("好好学习", "天天向上");
            String value = jedis.get("好好学习");
            System.out.println(value);
    
            //2. 判断 key 是否存在
            if(jedis.exists("好好学习")){jedis.set("好好学习", "天天敲代码");
            }else {jedis.set("好好学习", "天天开心");
            };
    
            //3. 为 key 增加超时工夫
            jedis.expire("好好学习", 10);
            Thread.sleep(2000);
            System.out.println("残余存活工夫:"+jedis.ttl("好好学习"));
    
            //4. 撤销剩余时间
            jedis.persist("好好学习");
            System.out.println("撤销胜利");
        }
    }
  2. 高级 API

    i. 测试 setnx()

    @Test
    // 如果数据不存在, 则批改数据
    public void testSetNx() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("key1", "aaa");
        // 如果 key 不存在则赋值
        jedis.setnx("key1", "测试方法");
        System.out.println(jedis.get("key1")); //"aaa"
    }

    ii. 测试 setex()

    @Test
    // 实现超时工夫的设定与赋值操作原子性
    public void testSetEx() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("key2", "bbb");
        jedis.expire("key2", 3);
        // 数据超时之后肯定会被删除吗? 错!
        // 考点: 为数据设定超时工夫时, 切记满足原子性操作, 否则会呈现 key 永远不能删除的景象
        jedis.setex("key2", 3, "bbb");
    }

    iii. 测试 SetParams

    @Test
    // 如果 key 存在, 则为其赋值并增加超时工夫
    public void testSet() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379);
        /*if(jedis.exists("key3")){jedis.setex("key3", 10, "cc");
        }*/
        //NX - 只有数据不存在时才会赋值 XX - 只有数据存在时才会赋值
        //EX - 设置超时工夫:s PX - 设置超时工夫:ms
        SetParams setParams = new SetParams();
        setParams.xx().ex(10);
        // 将所有的操作采纳原子性的形式进行管制
        jedis.set("key3", "ccc", setParams);
    }

    iv. 测试 Hash

    @Test
    public void testHash() {Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.hset("person", "id","100");
        jedis.hset("person","name","Tomcat");
        System.out.println(jedis.hgetAll("person"));
    }

    v. 测试 List

    @Test
    public void testList(){Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.lpush("list", "1","2","3","4");
        String value = jedis.rpop("list");
        System.out.println(value);
    }

    vi. 测试事务

    @Test
    public void testMulti(){Jedis jedis = new Jedis("192.168.126.129", 6379);
        // 开启事务
        Transaction transaction = jedis.multi();
        try {transaction.set("a", "a");
            transaction.set("b", "b");
            transaction.set("c", "c");
            // 提交事务
             transaction.exec();}catch (Exception e){
            // 回滚事务
            transaction.discard();}
    }
三. SpringBoot 整合 redis
  1. 编写 RedisConfig 配置类

    @Configuration // 标识一个配置类 个别与 @Bean 注解连用
    @PropertySource("classpath:/properties/redis.properties")
    public class RedisConfig {@Value("${redis.host}")
        private String host;
        @Value("${redis.port}")
        private Integer port;
    
        @Bean
        public Jedis jedis(){return new Jedis(host,port);
        }
    }
  2. 批改 TestRedis 测试类

    @SpringBootTest
    public class TestRedis {
        @Autowired
        private Jedis jedis;
        ...
    }
四. 基于业务实现缓存
  1. ObjectMapperUtil 实现

    i. 业务需要

    ​ 在业务中通常须要将业务对象

    ii. 入门案例 - 编写 TestObjectMapper 测试类

    public class TestObjectMapper {
        //objectMapper 入门案例
        @Test
        public void test01() throws JsonProcessingException {
            //1. 将对象转化为 JSON
            ItemDesc itemDesc = new ItemDesc();
            itemDesc.setItemId(100L).setItemDesc("测试数据")
                    .setCreated(new Date()).setUpdated(itemDesc.getCreated());
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(itemDesc);
            System.out.println(json);
    
            //2. 将 JSON 转化为对象
            ItemDesc desc = objectMapper.readValue(json, ItemDesc.class);
            System.out.println(desc);
        }
    }
    //objectMapper 入门案例
    @Test
    public void test02() throws JsonProcessingException {List<ItemDesc> list = new ArrayList<>();
        //1. 将汇合转化为 JSON
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("测试数据")
                .setCreated(new Date()).setUpdated(itemDesc.getCreated());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(50L).setItemDesc("测试数据 2")
                .setCreated(new Date()).setUpdated(itemDesc.getCreated());
        list.add(itemDesc);
        list.add(itemDesc2);
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(list);
        System.out.println(json);
    
        //2. 将 JSON 转化为汇合
        List<ItemDesc> desc = objectMapper.readValue(json, list.getClass());
        System.out.println(desc);
    }

    iii. 编辑 ObjectMapperUtil 工具 API

    public class ObjectMapperUtil {private static final ObjectMapper MAPPER = new ObjectMapper();
    
        private static String toJSON(Object target){
            try {return MAPPER.writeValueAsString(target);
            } catch (JsonProcessingException e) {e.printStackTrace();
                // 将查看异样转化为运行时异样
                throw new RuntimeException(e);
            }
        }
    
        private static <T> T toObj(String json,Class<T> clazz){
            try {return MAPPER.readValue(json, clazz);
            } catch (JsonProcessingException e) {e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
  2. 实现商品种类的缓存

    i. 编辑 ItemCatController

    /* 业务需要: 实现商品分类树形构造的展现
        url: http://localhost:8091/item/cat/list
        参数: id = 父级节点的 id
        返回值: List<EasyUITree>
    */
    @RequestMapping("/list")
    public List<EasyUITree> showTree(Long id){
        // 临时只查问一级菜单信息
        long pid = (id==null)?0L:id;
        // 数据库记录 ItemCat 对象, 页面中要的数据 EasyUITree
        // 须要将 pojo 对象转换为 vo 对象 get/set...
        //List<EasyUITree> list = itemCatService.findAllCat(pid);
        //return list;
        return itemCatService.findItemCatCache(pid);
    }

    ii. 编辑 ItemCatService/Impl

    /* 实现步骤:
        1. 先定义 key ITEM_CAT_PARENT::0
        2. 先查问缓存
            T - 通过获取缓存数据之后, 将 JSON 转化为对象, 之后返回
            F - 应该查询数据库, 之后将数据保留到 redis 中, 默认 30 天超时
     */
    @Autowired(required = false)
    private Jedis jedis;
    @Override
    public List<EasyUITree> findItemCatCache(long pid) {List<EasyUITree> treeList = new ArrayList<>();
        //1. 定义 key
        String key = "ITEM_CAT_PARENT::"+pid;
        //2.
        if(jedis.exists(key)){String json = jedis.get(key);
            System.out.println("== 查问缓存 ==");
            treeList =  ObjectMapperUtil.toObj(json,treeList.getClass());
        }else {
            // 不存在
            treeList = findAllCat(pid);
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.setex(key,7*24*60*60, json);
            System.out.println("== 查数据库 ==");
        }
        return treeList;
    }
  3. AOP 实现 Redis 缓存

    i. AOP 温习

    ​ 1) 利用 AOP 能够实现对办法 (性能) 的扩大,实现代码的解耦;

    ​ 2) 切面的组成因素:

    ​ 切面 = 切入点表达式 + 告诉办法

    ​ 3) 切入点表达式

    bean(bean 的 id) 拦挡 bean 的所有办法, 是粗粒度的 --> 具体的某个类
    within(包名. 类名) 扫描某个包下的某些类中的所有办法 com.jt.* 粗粒度的
    execution(返回值类型 包名. 类名. 办法名(参数列表)) 细粒度的 !!!
    @annotation(包名. 注解名) 细粒度的

    ​ 4) 告诉办法

    before 指标办法执行前
    afterReturning 指标办法返回后
    afterThrowing 指标办法抛出异样后
    after 不论什么状况, 最初都要执行
    around 指标办法执行前后都要执行

    ​ 前四种告诉类型个别用于记录程序运行的状态。如果要对程序运行的轨迹产生影响,首选 around。

    ii. AOP 入门案例 – 编写 CacheAop

    @Aspect
    @Component
    public class CacheAop {
        // 切面 = 切入点表达式 + 告诉办法
        // 表达式 1: ItemCatServiceImpl 类
        //@Pointcut("within(com.jt.service.*)")
        //.* 一级包下的类 ..* 所有子孙后代包中的类
        //@Pointcut("execution(* com.jt.service..*.*(**))")
        @Pointcut("bean(itemCatServiceImpl)") // l 默认值类名首字母小写
        public void pointcut(){}
    
        @Before("pointcut()")
        public void before(JoinPoint joinPoint){
            //jointPoint 代表连接点的对象 - 程序执行的办法实用于前四大类型
            //1. 获取指标对象
            Object target = joinPoint.getTarget();
            System.out.println(target);
            //2. 获取指标对象的门路 包名. 类名. 办法名
            String className = joinPoint.getSignature().getDeclaringTypeName();
            String methodName = joinPoint.getSignature().getName();
            System.out.println("指标办法的门路:"+className+methodName);
            //3. 获取参数类型
            System.out.println(Arrays.toString(joinPoint.getArgs()));
        }
    
        @Around("pointcut()")
        public Object around(ProceedingJoinPoint joinPoint){System.out.println("== 盘绕告诉 ==");
            Object data = null;
            try {data = joinPoint.proceed();
            } catch (Throwable throwable) {throwable.printStackTrace();
            }
            return data;
        }
    }
作业:通过自定义注解 @CacheFind 实现 AOP-redis 缓存
退出移动版