`<!--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 实现缓存查询!!!! }