AOP总结笔记

Posted summer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP总结笔记相关的知识,希望对你有一定的参考价值。

为什么使用AOP?
问题1: 将缓存的代码直接写到业务中,只对某个业务有效,如果其他业务也需要缓存,则需要重复编辑…
问题2: 如果将缓存代码直接写死在业务层,如果后期代码需要更新时,代码的耦合性高. 不便于扩展…
解决方案: 使用AOP方式实现…

名称: 面向切面编程.
核心作用: 在不改变原有代码的条件下 对方法进行功能扩展.
AOP构造要素 = 切入点表达式 + 通知方法.

spring中的两大核心机制:
1:控制反转(IOC)+依赖注入(DI)

控制反转:将对象创建的权力交给容器,由容器控制对象的生命周期
注入方式一:   通过bean的id进行注入。
             bean:被spring容器管理的对象称之为bean。
            bean的id为类名首小字母小写。(@Autowired+@Qualifier(“itemCatServiceImpl”))
            使用注解@Qualifier
            
注入方式二:    通过类型注入。 spring规定一般的接口都是单实现
        

2:AOP
常见切入点表达式
名称解释: 被Spring容器管理的对象称之为bean.

1). bean(“bean的Id”) 一般类名首字母小写 按照某个类匹配
2). within(“包名.类名”) 按照指定的类目录(多个类) 上述2个操作粒度较粗 按类匹配
3). execution(返回值类型 包名.类名.方法名(参数列表…)) 控制方法参数级别 粒度较细
4). annotation(“包名.注解名”) 按照特定的注解进行拦截. 主流语法

通知方法
1)@Before 目标方法执行前执行
2)@AfterReturning目标方法执行之后执行
3)@AfterThrowing 目标方法抛出异常时执行
4)@After 程序最后执行
上述四大通知类型 一般用来记录程序的执行的状态, 为监控系统提供数据的支持的 . 只能监控不能处理.
5)@Around 目标方法执行前后都要执行
环绕通知是5大通知中功能最为强大的. 可以控制目标方法是否执行.(控制业务是否操作).

@Component      //交给Spring容器管理
@Aspect         //标识AOP切面类
public class RedisAOP {

    //1.定义切入点表达式   粗粒度(类) 细粒度(方法)
    @Pointcut("bean(itemCatServiceImpl)")
    public void pointCut(){

    }

    //joinPoint连接点
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        Class targetClass = joinPoint.getTarget().getClass();
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        String ClassName = joinPoint.getSignature().getDeclaringTypeName();
        System.out.println("获取目标对象的类型:"+targetClass);
        System.out.println("获取目标参数:"+ Arrays.toString(args));
        System.out.println("获取目标方法名称:"+ methodName);
        System.out.println("获取目标类的路径:"+ ClassName);
        System.out.println("我是前置通知!!!");
    }
}
JoinPoint            用在非环绕通知中 

ProceedingJoinPoint  只能用在环绕通知(@Around)中

ProceedingJoinPoint  extends JoinPoint 

ProceedingJoinPoint  有proceed()方法,控制目标方法或其他通知方法的执行。
package com.wxz.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFind {
    String key();  //没有设置默认值,使用该注解时,该属性必须添加值
    int seconds() default 0;//设定超时时间,默认不超时
}
package com.wxz.aop;

import com.jt.annotation.CacheFind;
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.stereotype.Component;
import redis.clients.jedis.Jedis;


import java.util.Arrays;

@Component      //交给Spring容器管理
@Aspect         //标识AOP切面类
public class RedisAOP {

    @Autowired
    private Jedis jedis;

    //1. 定义切入点表达式     2.定义通知方法


    /**
     * 实现AOP缓存:
     *      1.准备key = 获取key的前缀 + "动态拼接参数"
     *      2.从redis中获取数据
     *              结果1:  没有数据,查询数据库,之后将数据保存到缓存中
     *              结果2:  有数据,  直接将缓存数据返回
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind) throws Throwable {
        String perkey = cacheFind.key();
        String args = Arrays.toString(joinPoint.getArgs());
        String key = perkey + "::" + args;
        Object result = null;
        //2.判断redis中是否有数据
        if(jedis.exists(key)){
           String json = jedis.get(key);
           //利用工具API动态获取返回值类型
           MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
           Class returnType = methodSignature.getReturnType();
           result = ObjectMapperUtil.toObj(json,returnType);
           System.out.println("查询Redis缓存!!!");
        }else{
            //表示缓存中没有数据,应该查询数据库动态获取
            result = joinPoint.proceed();    //调用下一个通知/目标方法
            //应该将数据保存到缓存中
            String json = ObjectMapperUtil.toJSON(result);
            if(cacheFind.seconds()>0){
                jedis.setex(key, cacheFind.seconds(), json);
            }else{
                jedis.set(key,json);
            }
            System.out.println("AOP查询数据库!!!");
        }
        return result;
    }

说明:
SpringMVC中,在环绕通知方法中,加入新的参数(注解对象)后可以很方便的拿到注解的属性值。且@annotation中的注解全限定名可写成参数中的对象名。
这个参数的位置一定要在ProceedingJoinPoint 之后(ProceedingJoinPoint 在参数列表中的第一个位置)。

以上是关于AOP总结笔记的主要内容,如果未能解决你的问题,请参考以下文章

用AOP统一记录日志(详细笔记入门)

用AOP统一记录日志(详细笔记入门)

Spring5框架 笔记总结

JAVA之AOP

切面 aop 笔记

Spring源码高级笔记之——Spring AOP应用