分布式补充知识 03.AOP和redis的结合使用

01.单看redis在springboot项目的使用:
先是修改controller类中,来使用缓存调用查找数据

@Controller
@ResponseBody
@RequestMapping("/item/cat")
public class ItemCatController {
@Autowired//从Spring容器中进行动态的注入
private ItemCatService itemCatService;
/**
* 需求:实现商品分类列表页面的呈现
* url:/item/cat/list
* 参数:parentId
* 返回值结果:List<EasyUITree>对象
*
* 异步树控件:
* 当点击父级节点时,会发起新的url请求,并且参数为父级节点id=xxx,然后
* 再去根据父级节点的parentId,去查询所有的子级节点
*/
@RequestMapping("/list")
public List<EasyUITree> findEasyUITreeByParentId(@RequestParam(value = "id",
defaultValue = "0")Long parentId){
//findEasyUITreeByParentId(parentId)使用该方法查询数据库
//return itemCatService.findEasyUITreeByParentId(parentId);
//使用缓存查询数据
return itemCatService.findItemCatListByCache(parentId);
}

在service接口中新增一个缓存的方法:

public interface ItemCatService {
List<EasyUITree> findItemCatListByCache(Long parentId);
}

在相关的service实现类中去写方法:

@Service
public class ItemCatServiceImpl implements ItemCatService {
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 缓存数据的要求:
* 1.redis保存的数据结构key:value结构 key唯一:包名.类名.方法名::第一个参数
* 2.java对象转化为String类型 java对象~json对象~字符串
*
* 缓存业务的实现:
* 1.准备查询redis的key
* 2.根据key查询redis缓存
* 3.没有数据 第一次查询,查询后将数据先保存到redis中
* 4.有数据 不是第一次查询
* 可以通过缓冲直接获取数据,返回给用户,节省了查询数据库的时间
*
*/
@Autowired
private Jedis jedis; //让Spring容器自动注入jedis对象
@Override
public List<EasyUITree> findItemCatListByCache(Long parentId) {
long startTime = System.currentTimeMillis();
//1. 准备动态变化的key
String key
="com.jt.service.ItemCatServiceImpl.findItemCatByCache::"+parentId;
String json = jedis.get(key);
List<EasyUITree> list=new ArrayList<>();
//2.判断当前数据是否为null
if(StringUtils.isEmpty(json)) {
//数据为null 查询数据库
list=findEasyUITreeByParentId(parentId);//一个一个的java对象
long endTime = System.currentTimeMillis();
String jsonData=ObjectMapperUtil.toJSON(list);//将list结合转化为json串
//将数据存储到redis中
jedis.set(key, jsonData);
System.out.println("第一次查询数据库"+(endTime-startTime));
}else {
//表示缓存中有数据,可以将数据转化为对象
list=ObjectMapperUtil.toObject(json, list.getClass());
long endTime = System.currentTimeMillis();
System.out.println("查询缓存时长为:"+(endTime-startTime));
}
return list;
}
}
@Override
	public List<EasyUITree> findEasyUITreeByParentId(Long parentId) {
		// TODO Auto-generated method stub
		List<itemCat> list=getItemCatList(parentId);
		List<EasyUITree> treelist = new ArrayList<>(list.size());
		for (itemCat itemcat : list) {
			String state=itemcat.getIsParent()?"closed":"open";
			EasyUITree easytree=new EasyUITree();
			easytree.setId(itemcat.getId()).setText(itemcat.getName()).setState(state);
			treelist.add(easytree);
		}
		return treelist;
	}
	

需要用到工具类:

package com.jt.util;

import com.fasterxml.jackson.databind.ObjectMapper;

public class ObjectMapperUtil {
	/**
	* 该工具方法,实现对象与json数据的转化
	*/
	private static final ObjectMapper mapper=new ObjectMapper();
	/**
	* 将对象转化为json
	*/
	public static String toJSON(Object obj) {
	try {
	String json = mapper.writeValueAsString(obj);
	return json;//如果没有异常就返回json
	} catch (Exception e) {
	e.printStackTrace();
	throw new RuntimeException();
	}
	}
	/**
	* 将json转换成java对象
	*/
	public static <T> T toObject(String json,Class<T> targetClass){
	try {
	T t= mapper.readValue(json, targetClass);
	return t;
	} catch (Exception e) {
	e.printStackTrace();
	throw new RuntimeException();
	}
	}
}

ObjectMapper类可以帮助我们快速的进行各个类型和Json类型的相互转换。
还有一个
Jedis类(这个是redis在java中的类型,用这个可以在java中使用redis,这个是自动用注解@bean来去管理)

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;
@PropertySource("classpath:/properties/redis.properties")
@Configuration//配置当前资源为配置类
public class RedisConfig {
	@Value("${redis.host}")
	private String node;
	@Bean
	public Jedis jedis() {
		String[] nodeArray=node.split(":");
		String  host=nodeArray[0];
		Integer port=Integer.parseInt(nodeArray[1]);
		return new Jedis(host,port);
	}
	

}

资源

#配置单台redis服务的配置信息
redis.node=192.168.126.128:6379 

02.AOP和redis联合使用
先去创建一个自定义的注解:

@Target(ElementType.METHOD) //该注解对方法有效
@Retention(RetentionPolicy.RUNTIME) //表示运行期有效
public @interface CacheFind {
/**
对于key的设定:
1.如果用户自己定义了key,那我们就用用户自己的key
2.如果没有没有定义key,则自动生成key:包名.类名.方法名::第一个参数
*/
//保存到redis中 key value 用户可以自己指定,也可以动态生成
public String key() default "";
public int seconds() default 0; //设定超时时间秒,0表示永不超时
}

在注解中属性是用()来修饰的,key和seconds都是属性。
default是默认值,有时候用户在使用注解的时候并没有对注解内的属性进行赋值。
切面类

@Aspect //标识切面
@Component//将对象交给容器管理,这里的是创建和管理
public class RedisAOP {
@Autowired
private Jedis jedis;
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) {
//1.动态的生成key,joinpoint是封装连接点的方法的对象,cacheFind是自定义的注解
String key=getKey(joinPoint,cacheFind);
//2.利用redis查询数据
String json = jedis.get(key);
Object returnObject=null;
//3.判断redis中的数据是否有值
if(StringUtils.isEmpty(json)) {
//如果json为null则表示第一次查询,应该查询数据库
try {
returnObject=joinPoint.proceed(); //让目标方法执行
//将数据获取之后存入到redis中,将数据转化为JSON
String objJSON = ObjectMapperUtil.toJSON(returnObject);
if(cacheFind.seconds()>0) {
//表示用户有注解属性的赋值操作,有超时时间
jedis.setex(key, cacheFind.seconds(), objJSON);//使用redis的操作setex
}else {//无设置超时时间
jedis.set(key, objJSON);
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println("AOP查询数据库!");
}else {
//redis中不为null,则可以直接返回数据
Class targetClass=getClass(joinPoint);
returnObject=ObjectMapperUtil.toObject(json, targetClass);
System.out.println("AOP查询缓存");
}
return returnObject;
}
//动态获取返回值类型
private Class getClass(ProceedingJoinPoint joinPoint) {
MethodSignature methodsignature = (MethodSignature)
joinPoint.getSignature();
return methodsignature.getReturnType();
}
//
/**
* 如果用户有key,则使用自己的key
* 如果没有,则动态生成key
* @param joinPoint
* @return
*/



private String getKey(ProceedingJoinPoint joinPoint,CacheFind cacheFind) {
if(!StringUtils.isEmpty(cacheFind.key())) {
//表示用户自己有key
return cacheFind.key();
}
//1.获取对象的类路径 包名.类名
String className=joinPoint.getSignature().getDeclaringTypeName();
//2.获取方法名
String methodName=joinPoint.getSignature().getName();
//3.动态获取第一个参数
String firstArgs = joinPoint.getArgs()[0].toString();
return className+"."+methodName+"::"+firstArgs;
}
}

joinPoint.getSignature():


package org.aspectj.lang;
 
public interface Signature {
    String toString();
 
    String toShortString();
 
    String toLongString();
 
    String getName();
 
    int getModifiers();
 
    Class getDeclaringType();
 
    String getDeclaringTypeName();

}

MethodSignature类的作用是表示一个方法的签名,包括方法名、参数类型和返回类型等信息。它可以用于反射、动态代理等场景中,帮助程序员获取和操作方法的相关信息。在Spring框架中,MethodSignature类也被广泛应用于AOP编程中,用于描述切点表达式中的方法签名信息。

你可能感兴趣的:(redis,分布式,java)