乐趣区

关于java:京淘项目Day12

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 实现缓存查问!!!!

}
退出移动版