AOP使用案例
如何进行数据库和Redis中的数据同步?/ 你在项目的那些地方使用了aop?
答:可以通过Aop操作来实现数据库和Redis中的数据同步。/ 通过Aop操作来实现数据库和Redis中的数据同步。
可以定义一个切面类,通过对控制器下的所有方法进行环绕通知。
数据同步有两种情况
- 一种是服务器接收get请求,首先从Redis中取,没有对应的key再执行方法从数据库中获取数据并添加到Redis中;
- 第二种情况是服务器接收写请求,包括增删改,这时就需要先对Redis中的数据进行扫描,对特定key对应的的数据进行删除清空,再执行方法修改数据库中的内容(没有考虑再次将数据库中的数据同步到Redis是因为:如果服务器接收到任一get请求,都会自动进行同步)
import cn.cnmd.redis.RedisService;
import com.alibaba.fastjson2.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class RedisCacheAspect {
private static Random random = new Random();
@Autowired
private RedisService redisService;
@Pointcut("execution(* cn.ctmd.electric.*.controller.*(..))")
private void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
String className = method.getDeclaringClass().getSimpleName();
String methodName = method.getName();
if (method.isAnnotationPresent(GetMapping.class)) {// get请求
Object[] args = pjp.getArgs();
String cacheKey = className + "::" + methodName + JSON.toJSONString(args);
if (Boolean.TRUE.equals(redisService.hasKey(cacheKey))) {
return redisService.get(cacheKey);
} else {
synchronized (this) {
if (Boolean.FALSE.equals(redisService.hasKey(cacheKey))) {
Object value = pjp.proceed();
long expireTime = Duration.ofMinutes(5).toMillis() + random.nextInt(1000);
redisService.set(cacheKey, value, expireTime, TimeUnit.MILLISECONDS);
return value;
} else {
return redisService.get(cacheKey);
}
}
}
} else {
if (method.isAnnotationPresent(PostMapping.class) || method.isAnnotationPresent(PutMapping.class) || method.isAnnotationPresent(DeleteMapping.class)) {
List<String> list = redisService.scan(className, 50);
if (list != null) {
redisService.delete(list.toString());
}
}
}
return pjp.proceed();
}
}
AOP
概念:面向切面编程
术语
- 连接点:被拦截到的程序的执行点(在spring中就是被拦截到的方法)
- 切入点:对需要进行拦截的条件的定义(某个位置)
- 通知、增强:为切入点添加二维的功能
- 目标对象:要被增强的对象
- 织入:将切面和业务逻辑对象连接起来,并创建通知代理的过程
- 代理:被织入后产生的结果类
- 切面:一个横切关注点的模块化(一个切面类的代称)
类型
- 前置通知
- 后置通知
- 环绕通知
- 异常抛出通知
- 最终通知(少见)
一个切面类
@Aspect
public class AspectJAdvice {
@Before(value = "execution(* com.qf.spring.aop.service..*(..))")
public void before(JoinPoint jp){
Object[] args = jp.getArgs(); //获取方法参数
Signature signature = jp.getSignature(); //获取签名
if(signature instanceof MethodSignature){ //如果签名是方法签名
Method method = ((MethodSignature) signature).getMethod(); //获取方法
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
System.out.println("准备执行方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args));
}
}
@AfterReturning(value = "execution(* com.qf.spring.aop.service..*(..))", returning = "returnValue")
public void after(JoinPoint jp, Object returnValue){
Object[] args = jp.getArgs(); //获取方法参数
Signature signature = jp.getSignature(); //获取签名
if(signature instanceof MethodSignature){ //如果签名是方法签名
Method method = ((MethodSignature) signature).getMethod(); //获取方法
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
System.out.println("执行完方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",得到返回值:" + returnValue);
}
}
@AfterThrowing(value = "execution(* com.qf.spring.aop.service..*(..))", throwing = "t")
public void exception(JoinPoint jp, Throwable t){
Object[] args = jp.getArgs(); //获取方法参数
Signature signature = jp.getSignature(); //获取签名
if(signature instanceof MethodSignature){ //如果签名是方法签名
Method method = ((MethodSignature) signature).getMethod(); //获取方法
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
System.out.println("执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + t.getMessage());
}
}
@Around("execution(* com.qf.spring.aop.service..*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();//获取方法的参数
Object target = pjp.getTarget(); //获取代理对象
Signature signature = pjp.getSignature(); //获取签名
if(signature instanceof MethodSignature) { //如果签名是方法签名
Method method = ((MethodSignature) signature).getMethod(); //获取被拦截的方法对象
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
try {
System.out.println("准备执行方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args));
Object returnValue = method.invoke(target, args);
System.out.println("执行完方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",得到返回值:" + returnValue);
return returnValue;
} catch (Throwable t){
System.out.println("执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + t.getMessage());
throw t;
}
}
return null;
}
}