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设计要点
全部评论

相关推荐

04-28 22:33
已编辑
门头沟学院 C++
点赞 评论 收藏
分享
05-01 22:41
中南大学 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务