AOP

连接点常用API

ProceedingJoinPoint只能环绕通知使用(ProceedingJoinPoint控制目标方法的执行),如果当做参数来用,必须位于参数的第一位

private void saveSysLog(ProceedingJoinPoint point){
 //1.获取目标类(就相当于知道了是哪一个类)
 Class<?> targetCls = point.getTarget().getClass();
 MethodSignature ms= (MethodSignature) point.getSignature();
 System.out.println("ms________________"+ms);
 String methodName = ms.getName();//再获取方法名
 //2.获取类里的方法(类里有很多方法 通过方法名和方法参数类型能够确定是哪一个方法)
 Method targetMethod=targetCls.getMethod(methodName,ms.getParameterTypes());
 //3.获取目标方法上的注解
 RequiredLog requiredLog = targetMethod.getAnnotation(RequiredLog.class);
 //4.获取注解上的值
if(requiredLog!=null){
 operation=requiredLog.value();
}
String method=targetCls.getName()+"."+methodName;
 Object[] paramsObj=point.getArgs();
 //转普通串
 //String params= Arrays.toString(paramsObj);
 //将参数转换为字符串
 String params=new ObjectMapper().writeValueAsString(paramsObj);
 }
public void before(JoinPoint joinPoint){
 //getSignature:获取是一个对象(里面包含方法的返回值 方法名 方法参数)
 String methodName = joinPoint.getSignature().getName();
 String typeName = joinPoint.getSignature().getDeclaringTypeName();
 System.out.println("目标方法的路径:"+typeName+"."+methodName);
 // getArgs:返回目标方法的参数
 Object[] args = joinPoint.getArgs();
 System.out.println("参数:"+ Arrays.toString(args));
 Class<?> methodClass = joinPoint.getTarget().getClass();
 System.out.println("目标对象类型:"+methodClass);

入门案例

@Aspect //我是一个aop的切面类
@Component//将类交给spring容器管理
public class CacheAOP {
 //公式=切入点表达式 + 通知方法
 /*
 *关于切入点表达式的使用说明
 * 粗粒度:
 *   1.bean(bean的Id)一个类 bean的Id指的是交给spring容易管理的类的类名小写 ,
 * 也可以bean(*ServiceImpl) 多个类
 *   2.within(包名.类名) 一个类/*代替就是多个
 */ 
 @Pointcut("bean(itemCatServiceImpl)")
 public void pointCut(){
 //定义切入点表达式 只为了占位
 }
 //定义前置通知,与切入点表达式进行绑定,注意绑定的是方法
 @Before("pointCut()")
 //等同于@Before("bean(itemCatServiceImpl") 区别:pointCut()表示切入点表达式的引用
 //适用于多个通知同用情况
 public void before(JoinPoint joinPoint){
 //getSignature:获取是一个对象(里面包含方法的返回值 方法名 方法参数)
 String methodName = joinPoint.getSignature().getName();
 String typeName = joinPoint.getSignature().getDeclaringTypeName();
 System.out.println("目标方法的路径:"+typeName+"."+methodName);
 // getArgs:返回目标方法的参数
 Object[] args = joinPoint.getArgs();
 System.out.println("参数:"+ Arrays.toString(args));
 Class<?> methodClass = joinPoint.getTarget().getClass();
 System.out.println("目标对象类型:"+methodClass);
 System.out.println("我是前置通知");
 }
 
 @Around("pointCut()")
public Object doaround(ProceedingJoinPoint joinPoint) {
 /**
 * ProceedingJoinPoint只能环绕通知使用(ProceedingJoinPoint控制目标方法的执行),
 * 如果当做参数来用,必须位于参数的第一位,
 */
 Object result=null;
 try {
 result = joinPoint.proceed();//执行下一个通知或目标方法
 } catch (Throwable throwable) {
 throwable.printStackTrace();
 }
 System.out.println("环绕通知结束");
 return result;
}
 
 }

AOP 实现 Redis缓存

编写配置类

image.png

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {
 @Value("${redis.host}")
 private String host;
 @Value("${redis.port}")
 private Integer port;
 @Bean
 public Jedis jedis(){
 return new Jedis(host,port);
 }
}

编写工具类

public class ObjectMapperUtil {
 private static final ObjectMapper mapper=new ObjectMapper();
 //1.将用户传递的数据转化为json
 public static String toJson(Object object){
 if(object==null) {
 throw  new RuntimeException("传递的数据为null 请检查");
 }
 try {
 String json = mapper.writeValueAsString(object);
 return json ;
 } catch (JsonProcessingException e) {
 e.printStackTrace();
 throw new RuntimeException(e);
 }
 }
 //要求用户传递什么样的类型,就返回什么样的对象
 public static <T> T toObject(String json,Class<T> targer){
 if (StringUtils.isEmpty(json)||targer==null) {
 throw new RuntimeException("参数不能为空");
 }
 try {
 return mapper.readValue(json, targer);
 } catch (JsonProcessingException e) {
 e.printStackTrace();
 throw new RuntimeException(e);
 }
 }}

自定义缓存注解

如何控制 哪些方法需要使用缓存?
解决方案:采用自定义注解的形式 进行定义 如果方法执行需要使用缓存,则标识
关于注解的说明:

 1.注解名称:
 2.属性参数:
      key:应该由用户自己手动添加 一般添加业务名称 之后动态拼接形成唯一的
      seconds:用户可以指定数据的超时时间
      
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFind {
 public String preKey();//用户标识key的前缀
 public int seconds() default 0;//如果用户不写表示不需要超时,如果写了以用户为准
}

开始使用注解

image.png

实现代码

@Aspect //我是一个aop的切面类
@Component//将类交给spring容器管理
public class CacheAOP {
 @Autowired
 private Jedis jedis;
 /**
 * 1.动态生成key preKey+用户参数数组
 * @param joinPoint
 * @return
 */
 @Around("@annotation(cacheFind)")
 public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){
 System.out.println("注解拦截");
 Object result=null;
 try {
 //1.拼接Redis存储数据的key
 Object[] args = joinPoint.getArgs();
 String key = cacheFind.preKey()+"::" + Arrays.toString(args);
 //2.查询redis
 if(jedis.exists(key)){
 //redis中有记录
 String json=jedis.get(key);
 //将数据转化成需要的类型——方法的返回值类型
 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
 Class type = signature.getReturnType();
 result = ObjectMapperUtil.toObject(json,type);
 }else{
 //不存在 查询数据库
 result=joinPoint.proceed();//执行目标方法
 //将查询结果保存到Redis中
 String json= ObjectMapperUtil.toJson(result);
 //判断数据是否需要超时时间
 if(cacheFind.seconds()>0){
 jedis.setex(key,cacheFind.seconds(),json);
 }else {
 jedis.set(key,json);
 }
 }
 } catch (Throwable throwable) {
 throwable.printStackTrace();
 }
 return result;
 }
}

Silver
13 声望11 粉丝