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()));
}
}