`<!--spring整合redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> </dependency>`
`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部蘋果12proMax 12000 1元秒殺? 提早預付活動費 10塊… 若是秒殺不成功 則7日內退還?java
`@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")); //隊列 }
`//弱事務控制 @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(); } }`
說明:因爲redis是公共的第三方,因此將配置放到jt-common中便可redis
說明: 須要在jt-common中添加redis的配置類spring
`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); } }
測試類的包路徑:數據庫
`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); } }`
`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); } } }
說明: 商品分類信息每次展開封閉的節點,都須要查詢數據庫.這樣的效率並不高. 可使用redis緩存來提高效率.
流程:
1.用戶第一次查詢先查詢緩存
2.緩存中沒有數據(這就是第一次查詢),查詢數據庫. 將數據庫記錄保存到緩存中便可.
3.緩存中有記錄. 直接經過緩存獲取數據以後返回便可.編程
`/** * 業務: 實現商品分類的查詢 * 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); }
`/** * 原理說明: * 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; }
問題1: 若是將業務代碼直接寫死,那麼該代碼不具備通用性.
問題2: 代碼冗餘 代碼的耦合性高.
AOP: 面向切面編程.
AOP做用: 在不修改原有方法的條件下.對原有的方法進行擴展.json
公式: AOP = 切入點表達式 + 通知方法後端
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))api
4.@annotation(包名.註解名稱) 按照註解匹配.
註解: @Find
@annotation(com.jt.anno.Find)緩存
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 實現緩存查詢!!!! }