cgb2010-京淘项目Day12

1.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 客户端操作String类型

`package com.jt.test;

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

//@SpringBootTest //目的:动态获取spring容器中的数据
public class TestRedis {

    /**
     * 主要目的测试程序远程操作Redis是否有效
     * 配置redis服务:
     *      1.redis需要关闭IP绑定模式
     *      2.redis关闭保护模式
     *      3.redis最好开启后端运行
     *
     * 完成redis客户端操作
     */
    @Test
    public void test01() throws InterruptedException {
        //1.测试链接
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("a", "动态获取redis中的数据");
        System.out.println(jedis.get("a"));

        //2.测试数据是否存在
        if(jedis.exists("a")){
            jedis.set("a", "修改数据");
        }else{
            jedis.set("a", "新增数据");
        }

        //3.删除redis
        jedis.del("a");

        //4.清空所有的数据
        jedis.flushDB();
        jedis.flushAll();

        //5.为数据添加超时时间
        jedis.set("b", "设定超时时间");
        jedis.expire("b", 10);
        Thread.sleep(2000);
        System.out.println(jedis.ttl("b"));
    }

    //原子性
    @Test
    public void test02(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.set("c", "测试redis");
        //需求1: 如果数据不存在时,才会为数据赋值.
        jedis.setnx("d","测试setnx方法");
        System.out.println(jedis.get("d"));

        //需求2: 需要为数据添加超时时间,同时满足原子性的要求
                //jedis.set("s", "为数据添加超时时间");
                //有时程序中断了,下列的方法将不会执行.
                //jedis.expire("s", 20);
                //System.out.println(jedis.ttl("s"));
        //为数据添加超时时间
        jedis.setex("s", 20, "为数据添加超时111");
        System.out.println("获取超时时间:"+jedis.ttl("s"));
    }

    /**
     *  需求: 如果数据存在才修改,并且为数据添加超时时间,满足原子性要求
     *  SetParams:
     *          XX: 数据存在时赋值.
     *          NX: 数据不存在时赋值
     *          EX: 添加超时时间单位秒
     *          PX: 添加超时时间单位毫秒
     */
    @Test
    public void test03(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.flushAll();
        SetParams setParams = new SetParams();
        setParams.xx().ex(20);
        jedis.set("a", "测试方法",setParams);
        System.out.println(jedis.get("a"));
    }
}` 

1.3 关于List集合说明

1.3.1 关于队列应用场景

秒杀场景: 马上过年了, 店铺周年店庆 1部苹果12proMax 12000 1元秒杀? 提前预付活动费 10块… 如果秒杀不成功 则7日内退还?

在这里插入图片描述

1.3.2 入门案例测试

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


1.4 关于事务控制

 `//弱事务控制
    @Test
    public void testTx(){
        Jedis jedis = new Jedis("192.168.126.129",6379);
        Transaction transaction = jedis.multi();  //开启事务
        try {
            transaction.set("k", "k");
            transaction.set("c", "c");
            transaction.exec();
        }catch (Exception e){
            e.printStackTrace();
            transaction.discard();
        }
    }` 

2 SpringBoot整合Redis

2.1 编辑pro配置文件

说明:由于redis是公共的第三方,所以将配置放到jt-common中即可
在这里插入图片描述

2.2 编辑配置类

说明: 需要在jt-common中添加redis的配置类

`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:/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);
    }

}

2.3 测试redis案例

测试类的包路径:
在这里插入图片描述

在这里插入图片描述

3 JSON转化工具API

3.1 入门案例测试

`package com.jt.test;

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 TestObjectMapper {

    @Test
    public void test01() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        //将对象转化为JSON  调用的是对象的get方法获取属性/属性的值
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(1000L).setItemDesc("对象与json转化")
                .setCreated(new Date()).setUpdated(new Date());
        String json = objectMapper.writeValueAsString(itemDesc);
        System.out.println(json);

        //将JSON串转化为对象 调用的是对象的set方法为对象属性赋值
        ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.getItemDesc());
    }

    @Test
    public void test02() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        //将对象转化为JSON  调用的是对象的get方法获取属性/属性的值
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(1000L).setItemDesc("对象与json转化").setCreated(new Date()).setUpdated(new Date());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(2000L).setItemDesc("对象与json转化2").setCreated(new Date()).setUpdated(new Date());

        List<ItemDesc> list2 = new ArrayList<>();
        list2.add(itemDesc);
        list2.add(itemDesc2);

        String json = objectMapper.writeValueAsString(list2);
        System.out.println(json);

        //将JSON串转化为对象 调用的是对象的set方法为对象属性赋值
        List list3 = objectMapper.readValue(json,list2.getClass());
        System.out.println(list3);
    }
}` 

3.2 封装工具API

`package com.jt.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.Item;
import com.jt.pojo.ItemDesc;
import com.sun.corba.se.spi.ior.IORTemplate;

/**
 * 该工具类,主要的功能实现对象与JSON串的互相转化.
 * 1.对象转化为JSON
 * 2.JSON转化为对象
 */
public class ObjectMapperUtil {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    //1.对象转化为JSON
    public static String toJSON(Object object){
        try {
            return MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    //2.JSON转化为对象 要求用户传递什么类型就返回什么对象??
    public static <T> T toObj(String json,Class<T> target){

        try {
            return MAPPER.readValue(json, target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

}

4 利用缓存实现商品分类查询

4.1 业务说明

说明: 商品分类信息每次展开封闭的节点,都需要查询数据库.这样的效率并不高. 可以使用redis缓存来提升效率.
流程:
1.用户第一次查询先查询缓存
2.缓存中没有数据(这就是第一次查询),查询数据库. 将数据库记录保存到缓存中即可.
3.缓存中有记录. 直接通过缓存获取数据之后返回即可.
在这里插入图片描述

4.2 编辑ItemCatController

 `/**
     * 业务: 实现商品分类的查询
     * URL地址: http://localhost:8091/itemCat/list?id=xxx
     * 请求参数: 传递节点的ID
     * 返回值:  List<EasyUITree>对象   页面JS-VO~~~~POJO--DB
     */
    @RequestMapping("/list")
    public List<EasyUITree> findItemCatList(Long id){
        //1.查询一级商品分类信息
        Long parentId = (id==null?0L:id);
        //return itemCatService.findItemCatList(parentId);
        //利用redis缓存查询数据
        return itemCatService.findItemCatCache(parentId);
    }

4.3 编辑ItemCatService

`/**
     * 原理说明:
     *      1.定义存取redis中的key  业务名称+标识符  ITEMCAT_PARENTID::0
     *      2.通过key获取redis中的记录
     *      3.空:    查询数据库 将返回值结果保存到缓存中即可
     *      4.非空    直接将缓存数据获取之后,返回给用户即可.
     * @param parentId
     * @return
     */
    @Override
    public List<EasyUITree> findItemCatCache(Long parentId) {
        long startTime = System.currentTimeMillis();
        String key = "ITEMCAT_PARENTID::" + parentId;
        List treeList = new ArrayList();
        if(jedis.exists(key)){
            //如果存在则直接返回
            String json = jedis.get(key);
            treeList = ObjectMapperUtil.toObj(json, treeList.getClass());
            System.out.println("查询Redis缓存!!!");
            long endTime = System.currentTimeMillis();
            System.out.println("耗时:"+(endTime - startTime)+"毫秒");
        }else{
            //如果不存在 则查询数据库.
            treeList = findItemCatList(parentId);
            //将数据保存到缓存中
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.set(key,json);
            System.out.println("查询数据库!!!");
            long endTime = System.currentTimeMillis();
            System.out.println("耗时:"+(endTime - startTime)+"毫秒");
        }
        return treeList;
    }

4.4 速度差

在这里插入图片描述

5 利用AOP实现商品分类缓存

5.1 为什么使用AOP

问题1: 如果将业务代码直接写死,那么该代码不具有通用性.
问题2: 代码冗余 代码的耦合性高.
AOP: 面向切面编程.
AOP作用: 在不修改原有方法的条件下.对原有的方法进行扩展.

5.2 关于AOP复习

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

5.2.1 通知方法

  1. before 目标方法执行之前执行
  2. afterThrowing 目标方法执行之后 抛出异常时执行
  3. afterReturning 目标方法执行之后 返回结果时执行
  4. after 目标方法执行之后执行(finally)
  5. around 环绕通知功能最为强大 可以控制目标方法的执行 在目标方法执行前后都要执行

5.2.2 切入点表达式

1.bean(bean的Id) 按照bean匹配!! Spring容器管理的对象称之为bean 粗粒度
2.within(包名.类名) 按照包路径匹配 其中可以使用通配符_代替
within("com.jt.service._ ") 位于com.jt.service中的包的所有的类都会匹配. 粗粒度
3.execution(返回值类型 包名.类名.方法名(参数列表)) 匹配的是方法参数级别 细粒度
execution(* com.jt.service._._(…)) 解释:返回值类型任意 在com.jt.service的包路径中的任意类的任意方法的任意参数…
execution( com.jt.service.userService.add(int,String))

4.@annotation(包名.注解名称) 按照注解匹配.
注解: @Find
@annotation(com.jt.anno.Find)

5.2.3 关于AOP案例

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 org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import java.util.Arrays;

/*@Service
@Controller
@Repository*/
@Component  //组件 将类交给spring容器管理
@Aspect     //表示我是一个切面
public class RedisAOP {

    //公式 aop = 切入点表达式   +   通知方法
    //@Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.*)")
    //@Pointcut("execution(* com.jt.service.*.*(..))")   //.* 当前包的一级子目录
    @Pointcut("execution(* com.jt.service..*.*(..))")  //..* 当前包的所有的子目录
    public void pointCut(){

    }

    //如何获取目标对象的相关参数?
    //ProceedingJoinPoint is only supported for around advice
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){    //连接点
        Object target = joinPoint.getTarget();
        Object[] args = joinPoint.getArgs();
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("目标对象:"+target);
        System.out.println("方法参数:"+Arrays.toString(args));
        System.out.println("类名称:"+className);
        System.out.println("方法名称:"+methodName);
    }

    //作业: 利用自定义注解@CacheFind 实现缓存查询!!!!

}

MrXx
4 声望12 粉丝

« 上一篇
京淘项目Day11
下一篇 »
京淘项目Day13