1. Redis入门案例

1.1 Redis常见用法

1.1.1 setex学习

/**
     * 2.需求:
     *      1.向redis中插入数据  k-v
     *      2.为key设定超时时间  60秒后失效.
     *      3.线程sleep 3秒
     *      4.获取key的剩余的存活时间.
     *
     *   问题描述: 数据一定会被删除吗??????
     *   问题说明: 如果使用redis 并且需要添加超时时间时 一般需要满足原子性要求.
     *   原子性:   操作时要么成功 要么失败.但是必须同时完成.
     */
    @Test
    public void test02() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.setex("宝可梦", 60, "小火龙 妙蛙种子");
        System.out.println(jedis.get("宝可梦"));

       /* Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("宝可梦", "小火龙 妙蛙种子");
        int a = 1/0;    //可能会出异常
        jedis.expire("宝可梦", 60);
        Thread.sleep(3000);
        System.out.println(jedis.ttl("宝可梦"));*/
    }

1.1.2 setnx

/**
     * 3.需求如果发现key已经存在时 不修改数据.如果key不存在时才会修改数据.
     *
     */
    @Test
    public void test03() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.setnx("aaaa", "测试nx的方法");
        /*if(jedis.exists("aaa")){
            System.out.println("key已经存在 不做修改");
        }else {
            jedis.set("aaa", "测试数据");
        }*/
        System.out.println(jedis.get("aaaa"));
    }

1.1.3 set 超时时间原子性操作

 /**
     * 需求:
     *  1.要求用户赋值时,如果数据存在则不赋值.  setnx
     *  2.要求在赋值操作时,必须设定超时的时间 并且要求满足原子性 setex
     */
    @Test
    public void test04() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        SetParams setParams = new SetParams();
        setParams.nx().ex(20);
        jedis.set("bbbb", "实现业务操作AAAA", setParams);
        System.out.println(jedis.get("bbbb"));


    }

1.1.4 list集合练习

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

1.1.5 redis事务控制

 @Test
    public void testTx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //1.开启事务
        Transaction transaction = jedis.multi();
        try {
            transaction.set("aa", "aa");
            //提交事务
            transaction.exec();
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            transaction.discard();
        }
    }

2.秒杀业务逻辑—分布式锁机制

6988 —1块 1部手机 20显示抢购成功 并且支付了1块钱…
问题:
1.tomcat服务器有多台
2.数据库数据只有1份
3.必然会出现高并发的现象.
如何实现抢购…

2.1常规锁操作

2.1.1 超卖的原因

在这里插入图片描述

2.1.2 同步锁的问题

说明:同步锁只能解决tomcat内部的问题,不能解决多个tomcat并发问题
在这里插入图片描述

2.1.3 分布式锁机制

思想:
1.锁应该使用第三方操作 ,锁应该公用.
2.原则:如果锁被人正在使用时,其他的用户不能操作.
3.策略: 用户向redis中保存一个key,如果redis中有key表示有人正在使用这把锁 其他用户不允许操作.如果redis中没有key ,则表示我可以使用这把锁.
4.风险: 如何解决死锁问题. 设定超时时间.
在这里插入图片描述

3. SpringBoot整合Redis

3.1 编辑配置文件 redis.pro

说明:由于该配置被其他的项目共同使用,则应该写到jt-common中.
在这里插入图片描述

3.2 编辑配置类

说明: 编辑redis配置类.将Jedis对象交给Spring容器进行管理.

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public Jedis jedis(){

        return new Jedis(host,port);
    }
}

3.3 对象与JSON转化 ObjectMapper介绍

3.3.1 简单对象转化

/**
     * 测试简单对象的转化
     */
    @Test
    public void test01() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息")
                .setCreated(new Date()).setUpdated(new Date());
        //对象转化为json
        String json = objectMapper.writeValueAsString(itemDesc);
        System.out.println(json);

        //json转化为对象
        ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.getItemDesc());
    }

3.3.2 集合对象转化

 /**
     * 测试集合对象的转化
     */
    @Test
    public void test02() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息1")
                .setCreated(new Date()).setUpdated(new Date());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(100L).setItemDesc("商品详情信息2")
                .setCreated(new Date()).setUpdated(new Date());
        List<ItemDesc> lists = new ArrayList<>();
        lists.add(itemDesc);
        lists.add(itemDesc2);
        //[{key:value},{}]
        String json = objectMapper.writeValueAsString(lists);
        System.out.println(json);

        //将json串转化为对象
        List<ItemDesc> list2 = objectMapper.readValue(json, lists.getClass());
        System.out.println(list2);
    }

3.4 编辑工具API

package com.jt.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.springframework.util.StringUtils;

public class ObjectMapperUtil {

    /**
     * 1.将用户传递的数据转化为json串
     * 2.将用户传递的json串转化为对象
     */
    private static final ObjectMapper MAPPER = new ObjectMapper();

     //1.将用户传递的数据转化为json串
    public static String toJSON(Object object){

        if(object == null) {
            throw new RuntimeException("传递的数据为null.请检查");
        }

        try {
            String json = MAPPER.writeValueAsString(object);
            return json;
        } catch (JsonProcessingException e) {
            //将检查异常,转化为运行时异常
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    //需求: 要求用户传递什么样的类型,我返回什么样的对象  泛型的知识
    public static <T> T toObj(String json,Class<T> target){
        if(StringUtils.isEmpty(json) || target ==null){
            throw new RuntimeException("参数不能为null");
        }
        try {
           return  MAPPER.readValue(json, target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

3.5 商品分类的缓存实现

3.5.1 实现步骤

1.定义Redis中的key key必须唯一不能重复. 存 取 key = “ITEM_CAT_PARENTID::70”
2.根据key 去redis中进行查询 有数据 没有数据
3.没有数据 则查询数据库获取记录 之后将数据保存到redis中 方便后续使用.
4.有数据 表示用户不是第一次查询 可以将缓存数据直接返回即可.

3.5.2 编辑ItemCatController

在这里插入图片描述

3.5.3 编辑ItemCatService

@Override
    public List<EasyUITree> findItemCatListCache(Long parentId) {
        //0.定义公共的返回值对象
        List<EasyUITree> treeList = new ArrayList<>();
        //1.定义key
        String key = "ITEM_CAT_PARENTID::"+parentId;
        //2.检索redis服务器,是否含有该key

        //记录时间
        Long startTime = System.currentTimeMillis();
        if(jedis.exists(key)){
            //数据存在
            String json = jedis.get(key);
            Long endTime = System.currentTimeMillis();
            //需要将json串转化为对象
            treeList = ObjectMapperUtil.toObj(json,treeList.getClass());
            System.out.println("从redis中获取数据 耗时:"+(endTime-startTime)+"毫秒");
        }else{
            //3.数据不存在  查询数据库
            treeList = findItemCatList(parentId);
            Long endTime = System.currentTimeMillis();
            //3.将数据保存到缓存中
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.set(key, json);
            System.out.println("查询数据库 耗时:"+(endTime-startTime)+"毫秒");
        }
        return treeList;
    }

3.5.4 使用Redis的速度差

在这里插入图片描述

4. AOP实现Redis缓存

4.1 如何理解AOP

名称: 面向切面编程
作用: 降低系统中代码的耦合性,并且在不改变原有代码的条件下对原有的方法进行功能的扩展.
公式: AOP = 切入点表达式 + 通知方法

4.2 通知类型

1.前置通知 目标方法执行之前执行
2.后置通知 目标方法执行之后执行
3.异常通知 目标方法执行过程中抛出异常时执行
4.最终通知 无论什么时候都要执行的通知
特点: 上述的四大通知类型 不能干预目标方法是否执行.一般用来做程序运行状态的记录.监控

5.环绕通知 在目标方法执行前后都要执行的通知方法 该方法可以控制目标方法是否运行.joinPoint.proceed(); 功能作为强大的.

4.3 切入点表达式

理解: 切入点表达式就是一个程序是否进入通知的一个判断(IF)
作用: 当程序运行过程中 ,**满足了切入点表达式时才会去执行通知方法,**实现业务的扩展.
种类(写法):
1. bean(bean的名称 bean的ID) 只能拦截具体的某个bean对象 只能匹配一个对象
lg: bean(“itemServiceImpl”)
2. within(包名.类名) within(“com.jt.service.*”) 可以匹配多个对象
粗粒度的匹配原则 按类匹配

	  3. execution(返回值类型 包名.类名.方法名(参数列表))   最为强大的用法
	  lg : execution(* com.jt.service..*.*(..))
	  		返回值类型任意  com.jt.service包下的所有的类的所有的方法都会被拦截.
	  4.@annotation(包名.注解名称)  按照注解匹配.

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

毕生所求无它,爱与自由而已