1. AOP说明
1.1 AOP作用
利用AOP可以实现对方法(功能)的扩展.实现代码的解耦.
1.2 切面组成要素
切面 = 切入点表达式 + 通知方法
1.2.1 切入点表达式
1).bean(bean的ID) 拦截bean的所有的方法 具体的某个类 粗粒度的.
2).within(包名.类名) 扫描某个包下的某个类 com.jt.* 粗粒度的.
3).execution(返回值类型 包名.类名.方法名(参数列表)) 细粒度的
4).@annotation(包名.注解名) 细粒度的
1.2.2 通知方法
说明: 通知相互之间没有顺序可言. 每个通知方法都完成特定的功能,切记AOP的通知方法与目标方法之间的顺序即可.
1).before 目标方法执行前
2).afterReturning 目标方法执行后
3).afterThrowing 目标方法执行抛出异常时执行.
4).after 不管什么情况,最后都要执行的.
上述四大通知类型,一般用来记录程序的运行状态的.(监控机制)
5).around 目标方法执行前后都要执行.
如果要对程序的运行轨迹产生影响,则首选around.
1.2.3 AOP入门案例
@Aspect //标识我是一个切面
@Component //将对象交给spring容器管理 cacheAOP
public class CacheAOP {
//切面 = 切入点表达式 + 通知方法
//表达式1: bean(itemCatServiceImpl) ItemCatServiceImpl类
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.*)") // .* 一级包下的类 ..* 所有子孙后代的包和类
@Pointcut("execution(* com.jt.service..*.*(..))")
//返回值类型任意, com.jt.service包下的所有类的add方法参数类型任意类型
//写参数类型时注意类型的大小写
public void pointcut(){}
/**
*
* joinPoint代表连接点对象,一般适用于前四大通知类型(除around之外的)
* 客人 路人
*/
@Before("pointcut()")
public void before(JoinPoint joinPoint){
//1.获取目标对象
Object target = joinPoint.getTarget();
System.out.println(target);
//2.获取目标对象的路径 包名.类名.方法名
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
System.out.println("目标方法的路径:"+(className+"."+methodName));
//3.获取参数类型
System.out.println(Arrays.toString(joinPoint.getArgs()));
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
System.out.println("环绕通知执行");
Object data = null;
try {
data = joinPoint.proceed(); //执行目标方法
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return data;
}
}
2 AOP实现redis缓存
2.1 业务需求
需要通过自定义注解的形式动态实现缓存操作.通过注解获取其中的key.超时时间.
2.2 自定义注解的用法
2.3 编辑CacheAOP
package com.jt.aop;
import com.jt.annotation.CacheFind;
import com.jt.pojo.ItemDesc;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import sun.misc.Cache;
import java.util.Arrays;
@Aspect //标识我是一个切面
@Component //将对象交给spring容器管理 cacheAOP
public class CacheAOP {
@Autowired
private Jedis jedis;
/**
* 实现思路:
* 1.动态获取key 用户自定义的前缀+用户的参数[0,xx]
* 2.判断key是否存在
* 存在: 直接从redis中获取数据 JSON, 需要将json转化为具体对象 按照返回值类型进行转化
* 不存在: 执行目标方法.获得返回值数据. 将返回值结果转化为JSON格式. 之后保存到缓存中.
* @param joinPoint
* @return
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws NoSuchMethodException {
Object result = null;
//1.获取key的前缀
String key = cacheFind.key();
//2.获取方法参数
String argString = Arrays.toString(joinPoint.getArgs());
key = key + "::" + argString;
try {
//3.判断缓存中是否有数据
if(jedis.exists(key)){
String json = jedis.get(key);
//5.获取返回值类型
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
result = ObjectMapperUtil.toObject(json,methodSignature.getReturnType());
System.out.println("AOP查询redis缓存");
}else{
//表示缓存中没有数据,执行目标方法
result = joinPoint.proceed();
String json = ObjectMapperUtil.toJSON(result);
//4.判断数据中是否有超时时间
if(cacheFind.seconds()>0){
jedis.setex(key,cacheFind.seconds(),json);
}else{
jedis.set(key,json);
}
System.out.println("AOP执行数据库调用!!!!!");
}
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
return result;
}
}
2.4 商品分类名称优化
优化商品分类名称的名称. 在业务层添加缓存注解.