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 告诉办法
- before 指标办法执行之前执行
- afterThrowing 指标办法执行之后 抛出异样时执行
- afterReturning 指标办法执行之后 返回后果时执行
- after 指标办法执行之后执行 (finally)
- 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 实现缓存查问!!!!
}