SpringBoot part4 day11

1 Redis

1.1入门案例

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

redisTemplate Spring封装jedis高级API
1 新建包/类
在这里插入图片描述
2 检查Redis是否正常启动:
在这里插入图片描述
Redis启动方式:redis-server redis.conf
需要关闭防火墙
3 编写代码

package com.jt.test;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.Jedis;

//@SpringBootTest  //容器提供对象,不需要底层提供不需要添加
public class TestRedis {
    @Test
    public void testRedis() throws InterruptedException {
        //连接服务:IP地址:端口号
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //测试之前先清空redis缓存
        jedis.flushAll();

        //1.存入数据
        jedis.set("key1", "天天向上");
        String value = jedis.get("key1");
        System.out.println(value);
        //2.判断数据是否存在
        if (jedis.exists("key1")) {
            jedis.set("key1", "好好学习,天天向上");
            System.out.println(jedis.get("key1"));
        } else {
            jedis.set("key1", "天天开心");

        }

        //3.为key添加超时时间
        jedis.expire("key1", 10);//超时时间10秒
        Thread.sleep(2000);      //线程阻塞2秒
        System.out.println("剩余的存活时间" + jedis.ttl("key1"));
        //4.撤销剩余超时时间
        jedis.persist("key1");
        System.out.println("撤销成功");
    }

}

Redis高级用法

//需求:如果数据不存在,则将数据修改
    @Test
    public void testSetNx(){
        Jedis jedis=new Jedis("192.168.126.129", 6379);
        jedis.set("key1", "aaaa");
        //如果key存在,则赋值
        jedis.setnx("key1", "测试方法");
        System.out.println(jedis.get("key1"));

    }
/**
     * 需求:实现超时时间的设定与赋值操作的原子性
     * 考点:为数据设定超时时间,切记满足原子性要求,否则出现key永远不能删除
     * */
    @Test
    public void testSetEx(){
        Jedis jedis=new Jedis("192.168.126.129", 6379);
       /* jedis.set("key2", "bbbb");
        jedis.expire("key2",3);*/
        //数据超时之后一定会被删除吗???  错的
        jedis.setex("key2", 10, "bbb");//方法是原子性的


    }

 /**
     * 需求:如果数据存在时才会修改数据,并且为数据添加超时时间10S(原子性)
     *参数说明:
     * NX:只有数据不存在,才赋值
     * XX: 只有数据存在,才会赋值
     * EX:秒
     * PX:毫秒
    */
    @Test
    public void testSet(){
        Jedis jedis=new Jedis("192.168.126.129", 6379);
        /*if(jedis.exists("key3")){
            jedis.setex("key3", 10, "cccc");
        }*/
        SetParams setParams=new SetParams();
        setParams.xx().ex(10);
       //保证原子性操作
       jedis.set("key3", "ccc",setParams );

    }

hash类型:

说明:可以用散列类型保存对象和属性值
例子:User对象{id:2,name:小明,age:19}
应用对象:一般在工作中存储的都是基于一个业务对象的数据
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

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

        }

list集合

说明:Redis中的List集合是双端循环列表,分别可以从左右两个方向插入数据.
List集合可以当做队列使用,也可以当做栈使用
队列:存入数据的方向和获取数据的方向相反
:存入数据的方向和获取数据的方向相同
在这里插入图片描述
list 取走后数据消失

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

set集合

在这里插入图片描述
在这里插入图片描述
将set1 set2 交集存入set中

在这里插入图片描述
获取集合的元素
在这里插入图片描述

Redis事务

说明:redis中操作可以添加事务的支持.一项任务可以由多个redis命令完成,如果有一个命令失败导致入库失败时.需要实现事务回滚.

在这里插入图片描述
在这里插入图片描述
事务是单台事务,分布式多台redis,不能控制事务
redis是单进程单线程操作,没有线程安全性问题

/**
    * 事务的控制
    * */
    @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.exec();
       }catch(Exception e){
           transaction.discard();
       }
    }

1.2Redis整合SpringBoot

1.2.1创建配置类
在这里插入图片描述
1.2.2创建properties配置文件
在这里插入图片描述

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//标识是一个配置类  一般与@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);
    }
}

1.2.3测试

@SpringBootTest  //容器提供对象,不需要底层提供不需要添加
public class TestRedis {
    @Autowired
    private Jedis jedis;
    @Test
    public void testRedis() throws InterruptedException {
        //连接服务:IP地址:端口号
        //Jedis jedis = new Jedis("192.168.126.129", 6379);
        //测试之前先清空redis缓存
        jedis.flushAll();

        //1.存入数据
        jedis.set("key1", "天天向上");
        String value = jedis.get("key1");
        System.out.println(value);
        //2.判断数据是否存在
        if (jedis.exists("key1")) {
            jedis.set("key1", "好好学习,天天向上");
            System.out.println(jedis.get("key1"));
        } else {
            jedis.set("key1", "天天开心");

        }

        //3.为key添加超时时间
        jedis.expire("key1", 10);//超时时间10秒
        Thread.sleep(2000);      //线程阻塞2秒
        System.out.println("剩余的存活时间" + jedis.ttl("key1"));
        //4.撤销剩余超时时间
        jedis.persist("key1");
        System.out.println("撤销成功");
    }
    }

1.3ObjectMapper

1.3.1业务要求
说明: 变化范围不大的数据,并且需要被频繁查询的数据 可以添加缓存,
常见用法: 省/市/县/乡, 商品分类信息
在这里插入图片描述
说明:由于redis中一般使用String数据类型保存业务数据.但是代码中java对象Redis没办法直接保存,所以需要中间的转化的过程.使用JSON方式进行数据中转.
List java对象 --------- JSON ------------ Redis中 使用String数据类型保存.
1.3.2入门案例

//ObjectMapper入门案例
    //1.将对象转化为JSON
    //2.将JSON转化为对象
    @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);//ItemDesc(itemId=100, itemDesc=我是一个测试)
                                 //只输出自己的类型,不会输出父类属性

    }
 @Test
    public void test01() 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(200L).setItemDesc("我是一个测试")
                .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转化为对象   底层是linkedHashMap,不可以用具体类型单个接
       List<ItemDesc> list2=objectMapper.readValue(json, list.getClass());

        System.out.println(list2);
    }

1.3.3包装方法ObjectMapperUtil工具API:

在这里插入图片描述

package com.jt.util;

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

public class ObjectMapperUtil {
    private static final ObjectMapper MAPPER=new ObjectMapper();
    //封装API将对象转化为JSON
    public static String toJSON(Object target){
        try {
           return  MAPPER.writeValueAsString(target);

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

    }



    //将JSONzhuan'hu转化为对象
    public static <T>T toObject(String json,Class<T> targetClass){
        try {
           return  MAPPER.readValue(json, targetClass);

        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

}

controller

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

        Long parentId = (id==null?0L:id);  //根据parentId=0 查询一级商品分类信息
        //Long  parentId = 0L;
        //return itemCatService.findItemCatListByParentId(parentId);  //版本号 1.0.2 调用次方法 开发人员为xxxx
        return itemCatService.findItemCatCache(parentId);
    }

ItemCatServiceImpl

 @Autowired(required = false)//不是必须的注入,类似于懒加载
    private Jedis jedis;

 /**
     * 实现步骤:
     * 1.先定义key ITEM_CAT_PARENT::0
     * 2.先查询缓存
     * 有  true  获取缓存数据之后,将json转化为对象,之后返回
     * 没有 false 应该查询数据库,之后将数据保存到redis中。默认30天超时
     * */

    @Override
    public List<EasyUITree> findItemCatCache(Long parentId) {
        //1.定义key
        String key="ITAM_CAT_PARENT::"+parentId;
        List<EasyUITree> treeList=new ArrayList<>();
        //2.从缓存中获取对象
        if(jedis.exists(key)){
            long start = System.currentTimeMillis();
             //存在  直接获取缓存数据之后转化为对象
            String json=jedis.get(key);
           treeList= ObjectMapperUtil.toObject(json, treeList.getClass());
           long end =System.currentTimeMillis();
           System.out.println("查询redis缓存获取数据"+(end-start)+"毫秒");

        }else {
            //不存在  应该先查询数据库,之后将数据保存到redis
            long start=System.currentTimeMillis();
           treeList =  findItemCatList(parentId);
           String json = ObjectMapperUtil.toJSON(treeList);
           jedis.setex(key, 7*24*60*60, json);
           long end =System.currentTimeMillis();
            System.out.println("查询数据库获取结果"+(end-start)+"毫秒");
        }
        
        return treeList;
    }

AOP
2.2.1 切入点表达式
1)bean(bean的ID) 拦截bean的所有方法 粗粒度 具体的某个类
2)within(包名.类名) 扫描某个包下某个类 com.jt.* 粗粒度
3)execution(返回值类型 包名.类名.方法名(参数列表)) 细粒度控制
4) annotation(包名.注解名) 标识注解的,只对注解有效 细粒度
2.2.2通知方法
说明:通知相互之间没有顺序而言
1)before 目标方法实行之前
2)around 目标方法执行前后都要执行
3)afterReturn 目标方法执行
4)afterThrowing 目标方法执行异常抛异常时执行
5)after 不管什么情况,最后都要执行
除了around其余四种通知类型,一般用于记录程序的运行状态(监控机制)
around 对程序运行的轨迹产生影响,首选around

2.3AOP入门案例

拦截需要特定参数
在这里插入图片描述

package com.jt.aop;

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

import java.util.Arrays;

@Aspect      //标识是一个切面
@Component  //交给容器管理
public class CacheAOP {
    //切面=切入点表达式+通知方法
    //表达式1:ItemCatServiceImpl类
    @Pointcut("bean(itemCatServiceImpl)")
    public void pointCut(){};
  /**
   *joinPoint  代表连接点对象,一般使用于除around之外的通知
   *ProceedingJoinPoint  只用于around通知
   * */
   @Before("pointCut()")
    public void before(JoinPoint joinPoint){
       //1.获取目标对象
       Object target=joinPoint.getTarget();
       System.out.println(target);
       //2.获取目标对象的路径  包名.类名
       String className=joinPoint.getSignature().getDeclaringTypeName();
       String method=joinPoint.getSignature().getName();
       System.out.println("目标方法的路径:"+(className+"."+method));
       //获取带参方法的参数
       System.out.println(Arrays.toString(joinPoint.getArgs()));
   }

}

package com.jt.aop;

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

import java.util.Arrays;

@Aspect      //标识是一个切面
@Component  //交给容器管理
public class CacheAOP {
    //切面=切入点表达式+通知方法
    //表达式1: @Pointcut("bean(itemCatServiceImpl)") ItemCatServiceImpl类
    //表达式2: @Pointcut("within(com.jt.service.*)")
    //表达式3: @Pointcut("execution(* com.jt.service.*add(..))") .*一级包下的类  ..*所有子孙后代的包和类
     //返回值类型任意   com.jt.service.下所有add方法
    //execution(* com.jt.service..*.*(long))  参数类型严格区分大小写
    @Pointcut("bean(itemCatServiceImpl)")
    public void pointCut(){};
  /**
   *joinPoint  代表连接点对象,一般使用于除around之外的通知
   *ProceedingJoinPoint  只用于around通知
   * */
   @Before("pointCut()")
    public void before(JoinPoint joinPoint){
       //1.获取目标对象
       Object target=joinPoint.getTarget();
       System.out.println(target);
       //2.获取目标对象的路径  包名.类名
       String className=joinPoint.getSignature().getDeclaringTypeName();
       String method=joinPoint.getSignature().getName();
       System.out.println("目标方法的路径:"+(className+"."+method));
       //获取带参方法的参数
       System.out.println(Arrays.toString(joinPoint.getArgs()));
   }

}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值