寧陵做網(wǎng)站雅虎搜索引擎中文版
AOP使用案例
如何進(jìn)行數(shù)據(jù)庫和Redis中的數(shù)據(jù)同步?/ 你在項(xiàng)目的那些地方使用了aop?
答:可以通過Aop操作來實(shí)現(xiàn)數(shù)據(jù)庫和Redis中的數(shù)據(jù)同步。/ 通過Aop操作來實(shí)現(xiàn)數(shù)據(jù)庫和Redis中的數(shù)據(jù)同步。
可以定義一個(gè)切面類,通過對控制器下的所有方法進(jìn)行環(huán)繞通知。
數(shù)據(jù)同步有兩種情況
- 一種是服務(wù)器接收get請求,首先從Redis中取,沒有對應(yīng)的key再執(zhí)行方法從數(shù)據(jù)庫中獲取數(shù)據(jù)并添加到Redis中;
- 第二種情況是服務(wù)器接收寫請求,包括增刪改,這時(shí)就需要先對Redis中的數(shù)據(jù)進(jìn)行掃描,對特定key對應(yīng)的的數(shù)據(jù)進(jìn)行刪除清空,再執(zhí)行方法修改數(shù)據(jù)庫中的內(nèi)容(沒有考慮再次將數(shù)據(jù)庫中的數(shù)據(jù)同步到Redis是因?yàn)?#xff1a;如果服務(wù)器接收到任一get請求,都會(huì)自動(dòng)進(jìn)行同步)
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();@Autowiredprivate 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
概念:面向切面編程
術(shù)語
- 連接點(diǎn):被攔截到的程序的執(zhí)行點(diǎn)(在spring中就是被攔截到的方法)
- 切入點(diǎn):對需要進(jìn)行攔截的條件的定義(某個(gè)位置)
- 通知、增強(qiáng):為切入點(diǎn)添加二維的功能
- 目標(biāo)對象:要被增強(qiáng)的對象
- 織入:將切面和業(yè)務(wù)邏輯對象連接起來,并創(chuàng)建通知代理的過程
- 代理:被織入后產(chǎn)生的結(jié)果類
- 切面:一個(gè)橫切關(guān)注點(diǎn)的模塊化(一個(gè)切面類的代稱)
類型
- 前置通知
- 后置通知
- 環(huán)繞通知
- 異常拋出通知
- 最終通知(少見)
一個(gè)切面類
@Aspect
public class AspectJAdvice {@Before(value = "execution(* com.qf.spring.aop.service..*(..))")public void before(JoinPoint jp){Object[] args = jp.getArgs(); //獲取方法參數(shù)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("準(zhǔn)備執(zhí)行方法:" + className + "." + methodName + ",參數(shù):" + Arrays.toString(args));}}@AfterReturning(value = "execution(* com.qf.spring.aop.service..*(..))", returning = "returnValue")public void after(JoinPoint jp, Object returnValue){Object[] args = jp.getArgs(); //獲取方法參數(shù)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("執(zhí)行完方法:" + className + "." + methodName + ",參數(shù):" + 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(); //獲取方法參數(shù)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("執(zhí)行方法時(shí):" + className + "." + methodName + ",參數(shù):" + Arrays.toString(args) + ",發(fā)生了異常:" + t.getMessage());}}@Around("execution(* com.qf.spring.aop.service..*(..))")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();//獲取方法的參數(shù)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("準(zhǔn)備執(zhí)行方法:" + className + "." + methodName + ",參數(shù):" + Arrays.toString(args));Object returnValue = method.invoke(target, args);System.out.println("執(zhí)行完方法:" + className + "." + methodName + ",參數(shù):" + Arrays.toString(args) + ",得到返回值:" + returnValue);return returnValue;} catch (Throwable t){System.out.println("執(zhí)行方法時(shí):" + className + "." + methodName + ",參數(shù):" + Arrays.toString(args) + ",發(fā)生了異常:" + t.getMessage());throw t;}}return null;}
}