1.AOP介绍

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2.AOP实现步骤

公式: AOP(切面) = 通知方法(5种) + 切入点表达式(4种)

3.通知复习

1.before通知              在执行目标方法之前执行
2.afterReturning通知      在目标方法执行之后执行
3.afterThrowing通知       在目标方法执行之后报错时执行
4.after通知               无论什么时候程序执行完成都要执行的通知
上述的4大通知类型,不能控制目标方法是否执行.一般用来**记录**程序的执行的状态.
一般应用与监控的操作.

5.around通知(功能最为强大的) 在目标方法执行前后执行.
因为环绕通知可以控制目标方法是否执行.**控制**程序的执行的轨迹.

4.切入点表达式

1.bean(“bean的ID”) 粒度: 粗粒度 按bean匹配 当前bean中的方法都会执行通知.
2.within(“包名.类名”) 粒度: 粗粒度 可以匹配多个类
3.execution(“返回值类型 包名.类名.方法名(参数列表)”) 粒度: 细粒度 方法参数级别
4.@annotation(“包名.类名”) 粒度:细粒度 按照注解匹配

5.AOP入门案例

@Aspect //标识我是一个切面
@Component  //交给Spring容器管理
public class CacheAOP {
    /**
 * 业务描述:
 * 获取方法路径
 * 获取方法参数
 * 获取方法名称
 *
 * 切入点表达式:(共4种【bean/whthin/execution/annotation】)
 * 返回值类型:空
 * 参数:JoinPoint
 * * 通知方法:(共5种【before/afterReturning/afterThrowing/after/around】)
 * 返回值类型:空/object
 * 参数:ProceedingJoinPoint
 */
    //切面 = 切入点表达式 + 通知方法
    //@Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.*)")   // .* 一级包路径   ..* 所有子孙后代包
    //@Pointcut("execution(返回值类型 包名.类名.方法名(参数列表))")
    @Pointcut("execution(* com.jt.service..*.*(..))")
    //注释: 返回值类型任意类型   在com.jt.service下的所有子孙类   以add开头的方法,任意参数类型
    public void pointCut(){

    }

    /**
     * 需求:
     *      1.获取目标方法的路径
     *      2.获取目标方法的参数.
     *      3.获取目标方法的名称
     */
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        String classNamePath = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("方法路径:"+classNamePath);
        System.out.println("方法参数:"+ Arrays.toString(args));
        System.out.println("方法名称:"+methodName);
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("环绕通知开始");
            Object obj = joinPoint.proceed();
            //如果有下一个通知,就执行下一个通知,如果没有就执行目标方法(业务方法)
            System.out.println("环绕通知结束");
            return null;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException(throwable);
        }

    }
}

6.AOP实现Redis缓存

1、业务实现策略
1).需要自定义注解CacheFind
2).设定注解的参数 key的前缀,数据的超时时间.
3).在方法中标识注解.
4).利用AOP 拦截指定的注解.
5).应该使用Around通知实现缓存业务

2、编辑自定义注解

@Target(ElementType.METHOD)         //注解对方法有效
@Retention(RetentionPolicy.RUNTIME) //运行期有效
public @interface CacheFind {
    public String preKey(); //定义key的前缀
    public int seconds() default 0;   //定义数据的超时时间.
}

3.方法中标识注解(ItemCatServiceImpl)
image.png

4.编辑CacheAOP

package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import lombok.extern.apachecommons.CommonsLog;
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 org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect //标识我是一个切面
@Component  //交给Spring容器管理
public class CacheAOP {
    @Autowired
    private Jedis jedis;
    /**
     * 注意事项:  当有多个参数时,joinPoint必须位于第一位.
     * 需求:
     *      1.准备key= 注解的前缀 + 用户的参数
     *      2.从redis中获取数据
     *         有: 从缓存中获取数据之后,直接返回值
     *         没有: 查询数据库之后再次保存到缓存中即可.
     *
     * 方法:
     *      动态获取注解的类型,看上去是注解的名称,但是实质是注解的类型. 只要切入点表达式满足条件
     *      则会传递注解对象类型.
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws Throwable {
        Object result = null;   //定义返回值对象
        String preKey = cacheFind.preKey();
        String key = preKey + "::" + Arrays.toString(joinPoint.getArgs());

        //1.校验redis中是否有数据
        if(jedis.exists(key)){
            //如果数据存在,需要从redis中获取json数据,之后直接返回
            String json = jedis.get(key);
            //1.获取方法对象,   2.获取方法的返回值类型
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //2.获取返回值类型
            Class returnType = methodSignature.getReturnType();
            result = ObjectMapperUtil.toObject(json,returnType);
            System.out.println("AOP查询redis缓存!!!");
        }else{
            //代表没有数据,需要查询数据库
            result = joinPoint.proceed();
            //将数据转化为JSON
            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;
    }

   /* @Around("@annotation(com.jt.anno.CacheFind)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //1.获取目标对象的Class类型
        Class targetClass = joinPoint.getTarget().getClass();
        //2.获取目标方法名称
        String methodName = joinPoint.getSignature().getName();
        //3.获取参数类型
        Object[] argsObj = joinPoint.getArgs();
        Class[]  argsClass = null;
        //4.对象转化为class类型
        if(argsObj.length>0){
           argsClass = new Class[argsObj.length];
            for(int i=0;i<argsObj.length;i++){
                argsClass[i] = argsObj[i].getClass();
            }
        }
        //3.获取方法对象
        Method targetMethod = targetClass.getMethod(methodName,argsClass);

        //4.获取方法上的注解
        if(targetMethod.isAnnotationPresent(CacheFind.class)){
            CacheFind cacheFind = targetMethod.getAnnotation(CacheFind.class);
            String key = cacheFind.preKey() + "::"
                         +Arrays.toString(joinPoint.getArgs());
            System.out.println(key);
        }

        Object object = joinPoint.proceed();
        System.out.println("环绕开始后");
        return object;
    }
*/

   /* //切面 = 切入点表达式 + 通知方法
    //@Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.*)")   // .* 一级包路径   ..* 所有子孙后代包
    //@Pointcut("execution(返回值类型 包名.类名.方法名(参数列表))")
    @Pointcut("execution(* com.jt.service..*.*(..))")
    //注释: 返回值类型任意类型   在com.jt.service下的所有子孙类   以add开头的方法,任意参数类型
    public void pointCut(){

    }*/

    /**
     * 需求:
     *      1.获取目标方法的路径
     *      2.获取目标方法的参数.
     *      3.获取目标方法的名称
     */
   /* @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        String classNamePath = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("方法路径:"+classNamePath);
        System.out.println("方法参数:"+ Arrays.toString(args));
        System.out.println("方法名称:"+methodName);
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("环绕通知开始");
            Object obj = joinPoint.proceed();
            //如果有下一个通知,就执行下一个通知,如果没有就执行目标方法(业务方法)
            System.out.println("环绕通知结束");
            return null;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException(throwable);
        }
    }*/
}

早起的鸟儿
7 声望2 粉丝

« 上一篇
Redis缓存学习
下一篇 »
jt-day14