1.Redis命令
1.1 入門案例操作
@Test
public void testHash() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.hset("person", "id", "18");
jedis.hset("person", "name", "hash測驗");
jedis.hset("person", "age", "2");
Map<String,String> map = jedis.hgetAll("person");
Set<String> set = jedis.hkeys("person"); //獲取所有的key
List<String> list = jedis.hvals("person");
}
@Test
public void testList() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.lpush("list", "1","2","3","4");
System.out.println(jedis.rpop("list"));
}
@Test
public void testTx(){
Jedis jedis = new Jedis("192.168.126.129",6379);
//1.開啟事務
Transaction transaction = jedis.multi();
try {
transaction.set("a", "a");
transaction.set("b", "b");
transaction.set("c", "c");
transaction.exec(); //提交事務
}catch (Exception e){
transaction.discard();
}
}
2 SpringBoot整合Redis
2.1 編輯pro組態檔
由于redis的IP地址和埠都是動態變化的,所以通過組態檔標識資料. 由于redis是公共部分,所以寫到common中.

2.2 編輯配置類
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 //標識我是配置類
@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);
}
}
3 物件與JSON串轉化
3.1 物件轉化JSON入門案例
package com.jt;
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.Date;
public class TestObjectMapper {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 1.物件如何轉化為JSON串的???
* 步驟:
* 1.獲取物件的所有的getXXXX()方法.
* 2.將獲取的getXXX方法的前綴get去掉 形成了json的key=xxx
* 3.通過getXXX方法的呼叫獲取屬性的值,形成了json的value的值.
* 4.將獲取到的資料 利用json格式進行拼接 {key : value,key2:value2....}
* 2.JSON如何轉化為物件???
* {lyj:xxx}
* 步驟:
* 1. 根據class引數型別,利用java的反射機制,實體化物件.
* 2. 決議json格式,區分 key:value
* 3. 進行方法的拼接 setLyj()名稱.
* 4.呼叫物件的setXXX(value) 將資料進行傳遞,
* 5.最終將所有的json串中的key轉化為物件的屬性.
*
*
* @throws JsonProcessingException
*/
@Test
public void testTOJSON() throws JsonProcessingException {
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("測驗資料轉化")
.setCreated(new Date()).setUpdated(new Date());
//1.將物件轉化為JSON
String json = MAPPER.writeValueAsString(itemDesc);
System.out.println(json);
//2.將json轉化為物件 src:需要轉化的JSON串, valueType:需要轉化為什么物件
ItemDesc itemDesc2 = MAPPER.readValue(json, ItemDesc.class);
/**
* 字串轉化物件的原理:
*/
System.out.println(itemDesc2.toString());
}
}
3.2 編輯ObjectMapper工具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();
//將物件轉化為JSON
public static String toJSON(Object target){
try {
return MAPPER.writeValueAsString(target);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
//將JSON轉化為物件
//需求: 如果用戶傳遞什么型別,則回傳什么物件
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);
}
}
}
4 實作商品分類快取實作
4.1 編輯ItemCatController
說明: 切換業務呼叫方法,執行快取呼叫
/**
* 業務: 實作商品分類的查詢
* url地址: /item/cat/list
* 引數: id: 默認應該0 否則就是用戶的ID
* 回傳值結果: List<EasyUITree>
*/
@RequestMapping("/list")
public List<EasyUITree> findItemCatList(Long id){
Long parentId = (id==null)?0:id;
//return itemCatService.findItemCatList(parentId);
return itemCatService.findItemCatCache(parentId);
}
4.2 編輯ItemCatService
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.ItemCatMapper;
import com.jt.pojo.ItemCat;
import com.jt.util.ObjectMapperUtil;
import com.jt.vo.EasyUITree;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
@Autowired(required = false) //程式啟動是,如果沒有改物件 暫時不加載
private Jedis jedis;
@Override
public String findItemCatName(Long itemCatId) {
return itemCatMapper.selectById(itemCatId).getName();
}
@Override
public List<EasyUITree> findItemCatList(Long parentId) {
//1.準備回傳值資料
List<EasyUITree> treeList = new ArrayList<>();
//思路.回傳值的資料從哪來? VO 轉化 POJO資料
//2.實作資料庫查詢
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id",parentId);
List<ItemCat> catList = itemCatMapper.selectList(queryWrapper);
//3.實作資料的轉化 catList轉化為 treeList
for (ItemCat itemCat : catList){
long id = itemCat.getId(); //獲取ID值
String text = itemCat.getName(); //獲取商品分類名稱
//判斷:如果是父級 應該closed 如果不是父級 則open
String state = itemCat.getIsParent()?"closed":"open";
EasyUITree easyUITree = new EasyUITree(id,text,state);
treeList.add(easyUITree);
}
return treeList;
}
/**
* Redis:
* 2大要素: key: 業務標識+::+變化的引數 ITEMCAT::0
* value: String 資料的JSON串
* 實作步驟:
* 1.應該查詢Redis快取
* 有: 獲取快取資料之后轉化為物件回傳
* 沒有: 應該查詢資料庫,并且將查詢的結果轉化為JSON之后保存到redis 方便下次使用
* @param parentId
* @return
*/
@Override
public List<EasyUITree> findItemCatCache(Long parentId) {
Long startTime = System.currentTimeMillis();
List<EasyUITree> treeList = new ArrayList<>();
String key = "ITEMCAT_PARENT::"+parentId;
if(jedis.exists(key)){
//redis中有資料
String json = jedis.get(key);
treeList = ObjectMapperUtil.toObject(json,treeList.getClass());
Long endTime = System.currentTimeMillis();
System.out.println("查詢redis快取的時間:"+(endTime-startTime)+"毫秒");
}else{
//redis中沒有資料.應該查詢資料庫.
treeList = findItemCatList(parentId);
//需要把資料,轉化為JSON
String json = ObjectMapperUtil.toJSON(treeList);
jedis.set(key, json);
Long endTime = System.currentTimeMillis();
System.out.println("查詢資料庫的時間:"+(endTime-startTime)+"毫秒");
}
return treeList;
}
}
5. AOP實作Redis快取
5.1 現有代碼存在的問題
1.如果直接將快取業務,寫到業務層中,如果將來的快取代碼發生變化,則代碼耦合高,必然重寫編輯代碼.
2.如果其他的業務也需要快取,則代碼的重復率高,開發效率低.
解決方案: 采用AOP方式實作快取.
5.2 AOP
在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期間動態代理實作程式功能的統一維護的一種技術,AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函式式編程的一種衍生范型,利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率,
5.3 AOP實作步驟
公式: AOP(切面) = 通知方法(5種) + 切入點運算式(4種)
5.3.1 通知復習
1.before通知 在執行目標方法之前執行
2.afterReturning通知 在目標方法執行之后執行
3.afterThrowing通知 在目標方法執行之后報錯時執行
4.after通知 無論什么時候程式執行完成都要執行的通知
上述的4大通知型別,不能控制目標方法是否執行.一般用來記錄程式的執行的狀態.
一般應用與監控的操作.
5.around通知(功能最為強大的) 在目標方法執行前后執行.
因為環繞通知可以控制目標方法是否執行.控制程式的執行的軌跡.
5.3.2 切入點運算式
1.bean(“bean的ID”) 粒度: 粗粒度 按bean匹配 當前bean中的方法都會執行通知.
2.within(“包名.類名”) 粒度: 粗粒度 可以匹配多個類
3.execution(“回傳值型別 包名.類名.方法名(引數串列)”) 粒度: 細粒度 方法引數級別
4.@annotation(“包名.類名”) 粒度:細粒度 按照注解匹配
5.3.3 AOP入門案例
package com.jt.aop;
import lombok.extern.apachecommons.CommonsLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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.Service;
import java.util.Arrays;
@Aspect //標識我是一個切面
@Component //交給Spring容器管理
public class CacheAOP {
//切面 = 切入點運算式 + 通知方法
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
//@Pointcut("within(com.jt.service.*)") // .* 一級包路徑 ..* 所有子孫后代包
//@Pointcut("execution(回傳值型別 包名.類名.方法名(引數串列))")
@Pointcut("execution(* com.jt.service..*.*(..))")
//注釋: 回傳值型別任意型別 在com.jt.service下的所有子孫類 以add開頭的方法,任意引數型別
public void pointCut(){
}
/**
* 需求:
* 1.獲取目標方法的路徑
* 2.獲取目標方法的引數.
* 3.獲取目標方法的名稱
*/
@Before("pointCut()")
public void before(JoinPoint joinPoint){
String classNamePath = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("方法路徑:"+classNamePath);
System.out.println("方法引數:"+ Arrays.toString(args));
System.out.println("方法名稱:"+methodName);
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
try {
System.out.println("環繞通知開始");
Object obj = joinPoint.proceed();
//如果有下一個通知,就執行下一個通知,如果沒有就執行目標方法(業務方法)
System.out.println("環繞通知結束");
return null;
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
}
}
5.4 AOP實作Redis快取
5.4.1 業務實作策略
1).需要自定義注解CacheFind
2).設定注解的引數 key的前綴,資料的超時時間.
3).在方法中標識注解.
4).利用AOP 攔截指定的注解.
5).應該使用Around通知實作快取業務.
5.4.2 編輯自定義注解
@Target(ElementType.METHOD) //注解對方法有效
@Retention(RetentionPolicy.RUNTIME) //運行期有效
public @interface CacheFind {
public String preKey(); //定義key的前綴
public int seconds() default 0; //定義資料的超時時間.
}
5.4.3 方法中標識注解

5.4.4 編輯CacheAOP
package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import lombok.extern.apachecommons.CommonsLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.lang.reflect.Method;
import java.util.Arrays;
@Aspect //標識我是一個切面
@Component //交給Spring容器管理
public class CacheAOP {
@Autowired
private Jedis jedis;
/**
* 注意事項: 當有多個引數時,joinPoint必須位于第一位.
* 需求:
* 1.準備key= 注解的前綴 + 用戶的引數
* 2.從redis中獲取資料
* 有: 從快取中獲取資料之后,直接回傳值
* 沒有: 查詢資料庫之后再次保存到快取中即可.
*
* 方法:
* 動態獲取注解的型別,看上去是注解的名稱,但是實質是注解的型別. 只要切入點運算式滿足條件
* 則會傳遞注解物件型別.
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws Throwable {
Object result = null; //定義回傳值物件
String preKey = cacheFind.preKey();
String key = preKey + "::" + Arrays.toString(joinPoint.getArgs());
//1.校驗redis中是否有資料
if(jedis.exists(key)){
//如果資料存在,需要從redis中獲取json資料,之后直接回傳
String json = jedis.get(key);
//1.獲取方法物件, 2.獲取方法的回傳值型別
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//2.獲取回傳值型別
Class returnType = methodSignature.getReturnType();
result = ObjectMapperUtil.toObject(json,returnType);
System.out.println("AOP查詢redis快取!!!");
}else{
//代表沒有資料,需要查詢資料庫
result = joinPoint.proceed();
//將資料轉化為JSON
String json = ObjectMapperUtil.toJSON(result);
if(cacheFind.seconds() > 0){
jedis.setex(key, cacheFind.seconds(), json);
}else{
jedis.set(key,json);
}
System.out.println("AOP查詢資料庫!!!");
}
return result;
}
/* @Around("@annotation(com.jt.anno.CacheFind)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//1.獲取目標物件的Class型別
Class targetClass = joinPoint.getTarget().getClass();
//2.獲取目標方法名稱
String methodName = joinPoint.getSignature().getName();
//3.獲取引數型別
Object[] argsObj = joinPoint.getArgs();
Class[] argsClass = null;
//4.物件轉化為class型別
if(argsObj.length>0){
argsClass = new Class[argsObj.length];
for(int i=0;i<argsObj.length;i++){
argsClass[i] = argsObj[i].getClass();
}
}
//3.獲取方法物件
Method targetMethod = targetClass.getMethod(methodName,argsClass);
//4.獲取方法上的注解
if(targetMethod.isAnnotationPresent(CacheFind.class)){
CacheFind cacheFind = targetMethod.getAnnotation(CacheFind.class);
String key = cacheFind.preKey() + "::"
+Arrays.toString(joinPoint.getArgs());
System.out.println(key);
}
Object object = joinPoint.proceed();
System.out.println("環繞開始后");
return object;
}
*/
/* //切面 = 切入點運算式 + 通知方法
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
//@Pointcut("within(com.jt.service.*)") // .* 一級包路徑 ..* 所有子孫后代包
//@Pointcut("execution(回傳值型別 包名.類名.方法名(引數串列))")
@Pointcut("execution(* com.jt.service..*.*(..))")
//注釋: 回傳值型別任意型別 在com.jt.service下的所有子孫類 以add開頭的方法,任意引數型別
public void pointCut(){
}*/
/**
* 需求:
* 1.獲取目標方法的路徑
* 2.獲取目標方法的引數.
* 3.獲取目標方法的名稱
*/
/* @Before("pointCut()")
public void before(JoinPoint joinPoint){
String classNamePath = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("方法路徑:"+classNamePath);
System.out.println("方法引數:"+ Arrays.toString(args));
System.out.println("方法名稱:"+methodName);
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
try {
System.out.println("環繞通知開始");
Object obj = joinPoint.proceed();
//如果有下一個通知,就執行下一個通知,如果沒有就執行目標方法(業務方法)
System.out.println("環繞通知結束");
return null;
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
}*/
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/213383.html
標籤:其他
