基于Spring AOP实现Redis缓存的配置

文章目录

    • Redis介绍
      • 业务需求
      • Redis是什么
      • Redis缓存服务器特点
    • 代码实现
      • Redis集群配置文件redis.properties
      • spring整合redis集群配置类RedisConfig.java
      • 工具API类ObjectMapperUtil.java
      • 切面表达式注解Cache_Find.java
      • 切面类CacheAspect.java
      • 业务代码中添加注解

Redis介绍

业务需求

在电商网站中,用户的查询的需求量远远高于更新的数据量,所以为了提高服务器响应的能力,需要添加缓存服务器。基于Spring AOP实现Redis缓存的配置_第1张图片

Redis是什么

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

Redis缓存服务器特点

  • 缓存数据结构采用k-v格式,key是唯一标识,value一般存储对象JSON数据
  • 缓存数据存储到内存中,速度是最快的(读:11.2万/秒;写:8.6万/秒;平均10万/秒)
  • 定期将缓存数据持久化到硬盘中
  • 为了维护内存大小,定期将缓存数据删除(lru算法、lfu算法)
  • 采用C语言编写

代码实现

Redis集群配置文件redis.properties

#配置redis集群
redis.nodes=192.168.72.130:7000,192.168.72.130:7001,192.168.72.130:7002,192.168.72.130:7003,192.168.72.130:7004,192.168.72.130:7005

spring整合redis集群配置类RedisConfig.java

 //redis配置类,相当于xml 
    @Configuration
    @PropertySource("classpath:/properties/redis.properties")//通过加载配置文件为属性赋值
    public class RedisConfig {
    	//spring整合redis集群
    	@Value("${redis.nodes}")
    	private String nodes;
    	
    	@Bean
    	public JedisCluster jedisCluster() {
    		Set<HostAndPort> nodeSet=new HashSet<>();
    		String[] nodeArray=nodes.split(",");
    		for (String node : nodeArray) {
    			String host=node.split(":")[0];
    			int port=Integer.parseInt(node.split(":")[1]);
    			nodeSet.add(new HostAndPort(host, port));
    		}
    		return new JedisCluster(nodeSet);
    	}
    }

工具API类ObjectMapperUtil.java

 package com.jt.util;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    /**
     * 对象与JSON相互转换工具API
     * @author Administrator
     */
    public class ObjectMapperUtil {
    	private static final ObjectMapper MAPPER=new ObjectMapper();
    	/**
    	 * 将对象转换成JSON
    	 */
    	public static String toJSON(Object object) {
    		String json=null;
    		try {
    			json=MAPPER.writeValueAsString(object);
    		} catch (Exception e) {
    			e.printStackTrace();
    			throw new RuntimeException();
    		}
    		return json;
    	}
    	
    	/**
    	 * 将JSON转换成对象
    	 */
    	public static <T>T toObject(String json,Class<T> targetClass) {
    		T object=null;
    		try {
    			object=MAPPER.readValue(json, targetClass);
    		} catch (Exception e) {
    			e.printStackTrace();
    			throw new RuntimeException();
    		}
    		return object;
    	}
    }

切面表达式注解Cache_Find.java

 package com.jt.anno;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**AOP缓存注解*/
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Cache_Find {
    	String key() default "";//存到缓存中的key,用户可以不写,如果为空串,表示自动生成key
    	int seconds() default 0;//缓存超时时间,0表示用户设置该数据不需要超时时间,如果不等于0则说明用户自己定义了超时时间
    }

切面类CacheAspect.java

package com.jt.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.druid.util.StringUtils;
import com.jt.anno.Cache_Find;
import com.jt.util.ObjectMapperUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.ShardedJedis;

@Component//交给spring管理
@Aspect//标识切面类     切面=切入点+通知
public class CacheAspect {
	@Autowired(required = false)//表示调用时才注入,相当于延时加载@Lazy
	//private Jedis jedis;//redis单片
	//private ShardedJedis jedis;redis分片
	//private Jedis jedis;//redis哨兵
	private JedisCluster jedis;//redis集群
	
	/**
	 * 环绕通知
	 * 1.返回值  必须为Object类型,表示执行完成业务之后返回给用户的数据
	 * 2.参数  a.必须位于第1位  b.参数类型必须为ProceedingJoinPoint(因为要用环绕通知控制目标方法执行,其他通知不能控制)
	 * 3.关于注解取值规则  springAOP中提供了可以直接获取注解的方法,但是要求参数的名称必须一致,否则映射错误
	 */
	//@Around("@annotation(com.jt.anno.Cache_Find)")//这样写下面就要通过反射获取注解的名称和参数key、seconds等
	@Around("@annotation(cacheFind)")
	public Object around(ProceedingJoinPoint joinPoint,Cache_Find cacheFind) {
		Object resultData=null;//结果数据,对象
		
		String key=getKey(joinPoint,cacheFind);
		String resultJSON=jedis.get(key);//从缓存中取数据,JSON串
		
		if (StringUtils.isEmpty(resultJSON)) {
			//表示缓存中没有数据则需要执行目标方法,从数据库查询数据
			try {
				resultData=joinPoint.proceed();//从数据库查询出来的数据,对象
				String value=ObjectMapperUtil.toJSON(resultData);//把数据库中查询出来的数据转换成JSON,要保存到缓存中
				//判断注解中是否要求数据永久保存,再保存到缓存中
				if (cacheFind.seconds()>0) {
					jedis.setex(key, cacheFind.seconds(), value);
				}else {
					jedis.set(key, value);
				}
				System.out.println("AOP查询数据库成功!");
			} catch (Throwable e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}else {
			//获取目标方法的返回值类型
			Class returnType=getReturnType(joinPoint);
			//表示缓存中有数据,将resultJSON转换成对象并传给resultData
			resultData=ObjectMapperUtil.toObject(resultJSON, returnType);
			System.out.println("AOP查询缓存成功!");
		}
		return resultData;
	}
	
	/**
	 * 获取目标方法的返回值类型
	 * @param joinPoint
	 * @return
	 */
	private Class getReturnType(ProceedingJoinPoint joinPoint) {
		MethodSignature signature=(MethodSignature)joinPoint.getSignature();
		return signature.getReturnType();
	}

	/**
	 * key生成策略:
	 * 1.如果用户在注解中指定了key,则使用用户指定的
	 * 2.如果用户没指定,在使用自动生成的key  类名+方法名+目标方法第一个参数(parentId)
	 * @param joinPoint
	 * @param cacheFind
	 * @return
	 */
	private String getKey(ProceedingJoinPoint joinPoint, Cache_Find cacheFind) {
		String key=cacheFind.key();//默认值 ""
		if (StringUtils.isEmpty(key)) {
			//如果为空就自动生成
			String className=joinPoint.getSignature().getDeclaringTypeName();//类名
			String methodName=joinPoint.getSignature().getName();//方法名
			String arg1=String.valueOf(joinPoint.getArgs()[0]);//目标方法第一个参数
			//com.jt.controller.list::0 拼接格式
			return className+"."+methodName+"::"+arg1;
		} else {
			return key;
		}
	}
}

业务代码中添加注解

 @RestController
    @RequestMapping("/item/cat")
    public class ItemCatController {
    	@Autowired
    	private ItemCatService itemCatService;
    	
    	//查询分类进行tree展示
    	@RequestMapping("/list")
    	@Cache_Find//配置redis缓存
    	public List<EasyUITree> findItemCatList(@RequestParam(defaultValue = "0",name = "id")Long parentId) {
    		return itemCatService.findEasyUITreeList(parentId);//从数据库查询
    	}
    }

你可能感兴趣的:(Spring,AOP,Spring,AOP,Redis,缓存)