Redis缓存层:AOP设计
业务需求:
在呈现商品列表的时候,先去查询缓存,如果缓存中没有目标数据的话,就去数据库获取目标数据,获取后将数据同步到Redis中,如果缓存中有数据的话直接返回目标数据
Redis配置文件:
redis.nodes=192.168.175.129:7000,192.168.175.129:7001,192.168.175.129:7002,192.168.175.129:7003,192.168.175.129:7004,192.168.175.129:7005
Redis配置类:
获取Redis配置信息,连接Redis集群
//表示redis配置类 @Configuration //xml @PropertySource("classpath:/properties/redis.properties") public class RedisConfig { //redis.nodes=192.168.175.129:7000,192.168.175.129:7001,192.168.175.129:7002,192.168.175.129:7003,192.168.175.129:7004,192.168.175.129:7005 @Value("${redis.nodes}") private String redisNodes; @Bean public JedisCluster jedisCluster() { Set<HostAndPort> nodes = new HashSet<>(); //1.根据,号拆分为多个node String[] strNode = redisNodes.split(","); //IP:端口 for (String node : strNode) { String host = node.split(":")[0]; int port = Integer.parseInt(node.split(":")[1]); HostAndPort hostAndPort = new HostAndPort(host, port); nodes.add(hostAndPort); } return new JedisCluster(nodes); } }
设计Redis注解:
@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 @Target(ElementType.METHOD) //表示该注解可以用于方法上 public @interface Cache_Find { String key() default ""; //接收用户key值 KEY_ENUM keyType() default KEY_ENUM.AUTO;//定义key类型 int secondes() default 0; //超时时间永不失效 }
设计RedisAOP:
@Component //将对象交给spring容器管理 @Aspect //标识切面 public class RedisAspect { @Autowired(required = false) //有该Bean直接注入,没有该Bean跳过不会报错 private JedisCluster jedis; @SuppressWarnings("unchecked") //表示切入点为 添加了@cache_find 的方法 //在该方法上形成环绕通知 @Around("@annotation(cache_find)") public Object around(ProceedingJoinPoint joinPoint,Cache_Find cache_find) { //1.获取key的值 String key = getKey(joinPoint,cache_find); //2.根据key查询缓存 String result = jedis.get(key); Object data = null; try { if(StringUtils.isEmpty(result)) { //如果结果为null,表示缓存中没有数据 //查询数据库 data = joinPoint.proceed(); //表示执行核心业务方法(此时就是添加了@cache_find的方法.) //将数据转化为JSON串,以便保存到Redis中 String json = ObjectMapperUtil.toJSON(data); //判断用户是否设定超时时间 if(cache_find.secondes() == 0) //表示永不超时 jedis.set(key, json); else //将数据从数据库同步到redis中 jedis.setex(key, cache_find.secondes(), json); System.out.println("第一次查询数据库!!!!"); }else { //如果缓存中有数据 Class targetClass = getClass(joinPoint); //添加了@Cache_find注解的方法所在的类 //如果缓存中有数据,则将json串转化为对象返回 data = ObjectMapperUtil.toObject(result, targetClass); System.out.println("AOP查询缓存!!!!!!"); } } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } return data; } //获取返回值类型 private Class getClass(ProceedingJoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); return signature.getReturnType(); } /** * 1.判断用户key类型 auto empty * @param joinPoint * @param cache_find * @return */ private String getKey (ProceedingJoinPoint joinPoint, Cache_Find cache_find) { //1.获取Key类型:通过注解获取redis KeyType:由于在定义注解的时候定义了该属性 //那么在使用该注解的时候就会传入该属性具体的值,此处就通过该注解获取到了传入的keyType KEY_ENUM key_ENUM = cache_find.keyType(); //2.判断key类型是否是枚举类中规定的类型 if(key_ENUM.equals(KEY_ENUM.EMPTY)) { //如果注解传入的keyType是枚举类型中的一种,就直接返回注解传入的key,也就是用户自己定义的key return cache_find.key(); } //如果注解传入的key类型不是枚举类中规定的类型 //表示用户的key需要拼接 key+"_"+第一个参数 //第一个参数就是 核心业务方法也就是加了@cache_find注解的方法传入的参数 String strArgs = String.valueOf(joinPoint.getArgs()[0]); String key = cache_find.key()+"_"+strArgs; return key; } }
核心业务:
//查询全部数据的商品分类信息 id=560 //需要获取任意名称的参数,为指定的参数赋值. //@RequestParam value/name 接收参数名称 defaultValue="默认值" required = true/false 是否必须传值 @RequestMapping("/list") @Cache_Find(key="ITEM_CAT",keyType=KEY_ENUM.AUTO) public List<EasyUITree> findItemCatByParentId(@RequestParam(value="id",defaultValue="0")Long parentId){ return itemCatService.findItemCatByParentId(parentId); }
枚举类:
//定义key的类型 public enum KEY_ENUM { AUTO, //使用用户自己的key //第一个参数 ITEM_CAT_0 EMPTY; //使用用户自己的key }
大体思路总结:
1.redis中key的设计要根据业务需求来设计,这里只是Demo
2.redis作为缓存层用AOP来设计会更加简便,设计注解后,想对什么核心业务方法添加Redis的缓存层就直接添加注解即可
3.AOP设计思路:
- 先获取配置文件的配置信息,封装到配置类中,实现对Redis集群的连接
- 设计缓存注解,并定义该注解使用的时候需要传入redis中的key以及key类型还有超时时间
- 然后设计Aspect切面,为切面中扩展业务添加环绕通知,在切面方法中获取注解传入的key的值以及key类型,根据业务对其进行加工
- 在该切面中先对缓存进行查询,如果缓存中没有目标就去数据库中获取目标数据,获取后将这些数据同步到缓存中,如果缓存中有目标数据直接将redis中的数据返回
4.需要在哪些核心业务中添加缓存层,就在核心业务方法上添加注解表明该方法为切入点,并在注解中传入需要的参数
AOP设计要点