cgb2010-京淘项目Day12
1.Redis入门案例
1.1 导入jar包
`
redis.clients
jedis
org.springframework.data
spring-data-redis
`
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 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 toObj(String json,Class 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对象 页面JS-VO~~~~POJO--DB
*/
@RequestMapping("/list")
public List 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 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 实现缓存查询!!!!
}