京淘项目实战开发-12[Redis入门案例、JSON格式]

1.Redis入门案例

Redis命令Redis概述与命令

1.1 导入jar包

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

1.2 入门案例

package com.jt.redis;

import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.params.SetParams;

import java.time.chrono.JapaneseDate;

public class TestRedis {

    /**
     * 1.redis配置文件3处修改
     * 2.Linux系统防火墙
     * 3.检查redis启动方式 redis-server redis.conf
     * */
    @Test
    public void testSet() throws InterruptedException {
        String host = "192.168.126.129"; //本地ip地址
        int port = 6379; //redis启动后的端口号
        Jedis jedis = new Jedis(host,port);
        //命令怎么敲,代码就怎么写
        jedis.set("2011", "redis入门案例"); //set添加
        System.out.println(jedis.get("2011"));//get获取数据
        jedis.flushAll();//清空全部数据库
        //判断数据是否存在:
        //测试是否存在
        if(jedis.exists("2011")){//判断数据是否存在
            jedis.del("2011");//删除redis中的数据
        }else{
            jedis.set("num", "100");//添加数据
            //自增1
            jedis.incr("num");//incr 自动加1
            jedis.expire("num", 20);//指定num的生效时间 单位秒
            Thread.sleep(2000);//睡眠2秒
            //ttl检查数据的剩余存活时间
            System.out.println(jedis.ttl("num"));//检查超时时间
            //取消超时时间
            jedis.persist("num");
        }
    }


    //有时我们可能会为数据添加超时时间
    //原子性问题: 有时业务要求要么同时完成,要么同时失败
    @Test
    public void testSetEx(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //jedis.set("a","aa");//存值
        //假设服务器异常,那么指定的生效时间则无法执行
        //jedis.expire("a",60);//指定a的生效时间 60秒
        jedis.setex("a", 20, "100");//setex把赋值操作和生效操作合二为一
        System.out.println(jedis.get("a"));
    }

    /**
     * 需求1:如果该数据不存在时,才会赋值.
     * 需求2: 如果数据存在时,进行修改, 并且为他设定超时时间 满足原子性要求.
     *
     */
    @Test
    public void testSetNX(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.set("a", "111");//存值
        jedis.setnx("a", "123");//指定的数据不存在时,为数据设置指定的值。
        System.out.println(jedis.get("a"));//获取a的值,并输出

//        需求2:
//        if (jedis.expire("a"){//数据存在
//            jedis.setex("a", 123, "60");//存值
//        }
        SetParams params = new SetParams();//创建对象
        params.xx().ex(60);//nx数据不存在 赋值,xx数据存在 赋值,ex 秒 px毫秒
        //判断数据是否存在,如果不存在就赋值
        jedis.set("b", "bb", params);//b存在,就赋值
    }

    // hash类型
    // 默认hash是无序的
    @Test
    public void testHash(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //hset 为对象添加数据
        jedis.hset("user", "id", "100");
        jedis.hset("user", "name", "qingfeng");
        jedis.hset("user", "age", "18");
        //hkeys 获取hash中的所有字段
        System.out.println(jedis.hkeys("user"));
        //hget获取对象的属性值
        System.out.println(jedis.hget("user", "name"));
        //hmget 获取hash里面指定字段的值
        System.out.println(jedis.hmget("user", "id"));
        //hgetAll 获取hash全部元素和值
        System.out.println(jedis.hgetAll("user"));
        //hstrlen 获取hash中指定key的值的长度
        System.out.println(jedis.hstrlen("user","name"));
    }

    //队列:先进先出   方向相反
    //栈: 先进后出    方向相同     注意可变参数类型
    @Test
    public void testList(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //L 4,3, 2,1 R
        //lpush从队列的左边入队一个或多个元素
        //jedis.lpush("list", "1,2,3,4"); 注意可变参数类型
        jedis.lpush("list", "1","2","3","4");
        //rpop 从队列的右端出队一个元素
        System.out.println(jedis.rpop("list"));
    }

    @Test
    public void testTx(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //开启事务
        //multi 标记一个事务开始
        Transaction transaction = jedis.multi();
        try {
            transaction.set("a", "a");
            transaction.set("b", "b");
            transaction.set("b", "b");
            //提交事务
            transaction.exec();//exec 执行所有multi之后发的命令
        }catch (Exception e){
            transaction.discard(); //discard丢弃所有multi之后发的命令,回滚
        }
    }
}

2.SpringBoot整合Redis

2.1 编辑pro配置文件

在这里插入图片描述

2.2 编辑配置类

package com.jt.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.Jedis;

@Configuration //标识为一个配置类,一般整合第三方
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {


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

    @Bean   //将该方法的返回值,交给Spring容器管理
    public Jedis jedis(){
        return new Jedis(host,port);
    }
}

使用方法:

    @Autowired
    private Jedis jedis;
    
    @Test
    public void testSpringBootRedis(){
        jedis.set("abc", "abc");
        System.out.println(jedis.get("abc"));
    }

3 JSON格式转化

3.1 业务说明

由于程序需要与JSON串进行数据的交互,所以通过API进行转化

3.2 代码如下

加深理解:

先在测试类中创建TestJSON方便理解
在这里插入图片描述

package com.jt.json;

        import com.fasterxml.jackson.core.JsonProcessingException;
        import com.fasterxml.jackson.databind.ObjectMapper;
        import com.jt.pojo.ItemDesc;
        import org.junit.jupiter.api.Test;

        import java.util.Date;

public class TestJOSN {
    /**
     * json格式:
     *  1.object格式  {key:value,key2:value2}
     *  2.数组格式    [value,value2,value3]
     *  3.嵌套格式    value可以进行嵌套.
     *
     * 任务:  实现对象与JSON串之间的转化.
     * 面试题: 常量是否有线程安全性问题?
     * (没有,多个线程共享资源,加了final不能被修改)
     */

    //定义常量,final不能被修改,不加final可能会有安全性问题
    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Test
    public void ObjectToJSON() throws JsonProcessingException {
        //创建对象
        ItemDesc itemDesc = new ItemDesc();
        //给对象赋值
        itemDesc.setItemId(100L)
                .setItemDesc("JSON测试")
                .setCreated(new Date())
                .setUpdated(itemDesc.getCreated());
        //将对象转换为JSON,调用的对象get方法,形成属性/属性值
        String json = MAPPER.writeValueAsString(itemDesc);
        System.out.println(json);
        //将JSON串,转化为对象 反射的机制 反射会实例化对象
        //怎么给对象赋值?调用对象的set方法为属性赋值
        ItemDesc itemDesc2 = MAPPER.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.toString()+""+itemDesc2.getCreated());
    }
}

运行测试:
在这里插入图片描述


加深结构理解:

package com.jt.json;

        import com.fasterxml.jackson.core.JsonProcessingException;
        import com.fasterxml.jackson.databind.ObjectMapper;
        import com.jt.pojo.ItemDesc;
        import org.junit.jupiter.api.Test;

        import java.util.ArrayList;
        import java.util.Date;
        import java.util.List;

public class TestJOSN {
    /**
     * json格式:
     *  1.object格式  {key:value,key2:value2}
     *  2.数组格式    [value,value2,value3]
     *  3.嵌套格式    value可以进行嵌套.
     *
     * 任务:  实现对象与JSON串之间的转化.
     * 面试题: 常量是否有线程安全性问题?
     * (没有,多个线程共享资源,加了final不能被修改)
     */

    //定义常量,final不能被修改,不加final可能会有安全性问题
    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Test
    public void ObjectToJSON() throws JsonProcessingException {
        //创建对象
        ItemDesc itemDesc = new ItemDesc();
        //给对象赋值
        itemDesc.setItemId(100L)
                .setItemDesc("JSON测试")
                .setCreated(new Date())
                .setUpdated(itemDesc.getCreated());
        //将对象转换为JSON,调用的对象get方法,形成属性/属性值
        String json = MAPPER.writeValueAsString(itemDesc);
        System.out.println(json);
        //将JSON串,转化为对象 反射的机制 反射会实例化对象
        //怎么给对象赋值?调用对象的set方法为属性赋值
        ItemDesc itemDesc2 = MAPPER.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.toString()+""+itemDesc2.getCreated());
    }

    //2.加深印象:
    //新建测试数据,封装到List集合
    public List<ItemDesc> getList(){
        List<ItemDesc> list = new ArrayList<>();
        ItemDesc itemDesc = new ItemDesc();
        //第一个对象
        itemDesc.setItemId(100L)
                .setItemDesc("bbb")
                .setCreated(new Date())
                .setUpdated(new Date());
        //第二个对象
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(100L)
                .setItemDesc("bbb")
                .setCreated(new Date())
                .setUpdated(new Date());

        list.add(itemDesc);
        list.add(itemDesc2);
        return list;
    }

    @Test
    public void ListToJSON() throws JsonProcessingException {
        List<ItemDesc> list = getList();
        //转换为JSON串
        String json = MAPPER.writeValueAsString(list);
        //输出JSON串
        System.out.println(json);

        //将JSON还原回对象
        List<ItemDesc> list2 = MAPPER.readValue(json, List.class);//传的是集合,转换回来还是集合
        System.out.println(list2);
    }
}

测试运行结果:
在这里插入图片描述

3.3 JSON转化工具API

说明: 在jt-common中添加工具API
在这里插入图片描述

工具类不需要交给容器管理

package com.jt.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class ObjectMapperUtil {

    //创建一个Object对象,通过这个对象进行数据的转换
    private static final ObjectMapper MAPPER = new ObjectMapper();

    //1.对象转化为JSON
    public static String toJSON(Object obj){
        try {
            //没有问题就return把数据返回
            return MAPPER.writeValueAsString(obj);

        } catch (JsonProcessingException e) {
            //将检查异常,转化成运行时异常
            e.printStackTrace();
            //抛出异常
            throw new RuntimeException(e);
        }
    }

    //2.JSON转化为对象
    //传递什么样的类型,返回什么样的对象???
    //<T> 类型的定义
    public static <T> T toObject(String json, Class<T> target){// Class<T> target 泛型
        try {
            //JSON转化为对象
            return MAPPER.readValue(json, target);

        } catch (JsonProcessingException e) {
            //将检查异常,转化成运行时异常
            e.printStackTrace();
            //抛出异常
            throw new RuntimeException(e);
        }
    }
}

4 商品分离缓存实现

4.1 编辑工具ItemCatServiceImpl

修改 ItemCatController

  @RequestMapping("/item/cat/list")
    public List<EasyUITree> findItemCatList(Long id){

        //查询商品分类信息  1级菜单
        //如果用户没有点击按钮 将不会传递Id值,应该设定默认值
        long parentId = (id==null?0:id); //0 一级菜单 判断是否点击,点击了就传值
//        return itemCatService.findItemCatList(parentId);
        return itemCatService.findItemCatCache(parentId);
    }

 /**
     *
     * 1.将商品分类信息添加到缓存中
     * 2.数据结构:  k-v结构   ITEM_CAT_PARENT::0 - JSON
     * 3.业务流程:
     *      1.先查缓存    true/false
     *      2.false  用户第一次查询数据库, 将结果保存到redis缓存中
     *      3.true   查询缓存
     *  AOP机制
     */
    @Autowired(required = false)
    private Jedis jedis;//该参数不是最终结构,后期需要优化 

    @Override
    public List<EasyUITree> findItemCatCache(long parentId) {
        //起始时间
        long startTime = System.currentTimeMillis();
        //准备一个空集合
        List<EasyUITree> treeList = new ArrayList<>();

        //1.定义key
        String key = "ITEM_CAT_PARENT::" + parentId;

        //2.判断数据是否存在
        if(jedis.exists(key)){
            //根据key查询json
            String json = jedis.get(key);
            //json转化为对象
            treeList = ObjectMapperUtil.toObject(json, treeList.getClass());
            //结束查询时间
            long endTime = System.currentTimeMillis();
            //输出查询结果
            System.out.println("redis查询! 耗时:"+(endTime-startTime));

        }else{
            //数据不存在,查询数据库
            treeList = findItemCatList(parentId);
            //将数据库记录,转化为JSON
            String json = ObjectMapperUtil.toJSON(treeList);
            // 将数据保存到redis中
            jedis.set(key, json);
            //结束查询时间
            long endTime = System.currentTimeMillis();
            //输出查询结果
            System.out.println("查询数据库!! 耗时:"+(endTime-startTime));
        }
        return treeList;
    }

4.2 Redis效果展现

在这里插入图片描述


5 Spring中AOP复习

5.1 什么是AOP

名称解释: 面向切面编程

作用: 在不修改源码的条件下,对方法 功能进行了额外了扩展.

意义: 降低了代码的耦合性.

公式: AOP = 切入点表达式 + 通知方法

5.2 切入点表达式

1.bean(“包名.类名”) 具体某个类 按类匹配 粗粒度

2.within(“包名.类名”) 可以使用通配符 几个类一起匹配 粗粒度

3.execution(返回值类型 包名.类名.方法名(参数列表)) 细粒度

4.注解方式 @annotation(注解的类型) 细粒度

5.3 通知方法

1.before 目标方法执行之前执行.

2.afterReturning 目标方法执行之后执行

3.afterThrowing 目标方法抛出异常时执行

4.after 不管上述操作如何运行,最终都要执行.

上述的4大通知,都不能控制目标方法是否执行,一般只会做程序的监控.

5.around 在目标方法执行前后 都要执行. 可以控制目标方法的执行轨迹.

5.4 AOP入门案例

在这里插入图片描述

5.5 代码测试理解

把控制类 ItemCatController 刚刚修改的内容还原

    @RequestMapping("/item/cat/list")
    public List<EasyUITree> findItemCatList(Long id){

        //查询商品分类信息  1级菜单
        //如果用户没有点击按钮 将不会传递Id值,应该设定默认值
        long parentId = (id==null?0:id); //0 一级菜单 判断是否点击,点击了就传值
        return itemCatService.findItemCatList(parentId);
        //return itemCatService.findItemCatCache(parentId);
    }

在jt_common中创建RedisAOP 类

package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component //将我们的对象交给Spring容器管理
@Aspect //表示AOP切面
public class RedisAop {
    /**
     * 1.定义切入点表达式
     * bean: 被spring容器管理的对象称之为 bean
     * 1.1 bean(bean的ID) 按类匹配  1个
     *      bean(itemCatServiceImpl)
     *
     * 1.2 within(包名.类名) 按类匹配  一堆
     *      within(com.jt.service.*)
     *
     * 1.3 execution(返回值类型 包名.类名.方法名(参数列表))
     *     execution(* com.jt.service..*.*(..)) 万能的表达式
     *     service. 一级
     *     service.. 多级
     *     解释:  返回值为任意类型 com.jt.service包所有的子孙包的类
     *            类中的任意方法,任意参数
     *     execution(Integer com.jt.service..*.add*(int)) //只能是包装类型
     *     execution(int com.jt.service..*.add*(int)) 只能是基本类型
     */

    //@Pointcut("execution(* com.jt.service..*.*(..))")
    @Pointcut("bean(itemCatServiceImpl)")
    public void pointCut(){

    }

    //Aop如何与目标方法进行关联? JoinPoint(连接点)
    //如何理解什么是连接点? 被切入点拦截的方法(连接点=方法)
    //ProceedingJoinPoint is only supported for around advice
    //只有环绕通知可以控制目标方法
    @Before("pointCut()") //乌龟的屁股 规定
    public void before(JoinPoint joinPoint){ //前置通知
        //获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        //获取目标类的名称
        String className = joinPoint.getSignature().getDeclaringTypeName();
        //获取目标的参数
        Object[] args = joinPoint.getArgs();
        //获取目标的对象
        Object target = joinPoint.getTarget();
        //输出
        System.out.println(methodName);
        System.out.println(className);
        System.out.println(args);
        System.out.println(target);
        System.out.println("我是一个前置通知");
    }
    
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕开始");
        Object result = joinPoint.proceed(); //执行下一个通知,目标方法
        System.out.println("环绕结束");
        return result;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值