山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(五)- Spring学习(3)

7. AOP 面向切面编程

SpringAOP : 批量对Spring容器中的Bean的方法做增强,并且这种增强不会与原来方法中的代码耦合

7.1 快速入门

需求: 要求所有类在调用前都输出 :方法被调用了

7.1.1 添加依赖
7.1.2

开启组件扫描创建切面类 在类上加上 @Component和@Aspect

  
  @Component
  @Aspect
  public class MyAspect {
  ​
     
  }

使用@PointCut注解指定要增强的方法

  
      @Pointcut("execution(* com.sangeng.service..*.*(..))")
      public void pt(){
      }

方法上加上before

  
      @Before("pt()")
      public void methodbefore(){
          System.out.println("方法被调用了");
      }

<br/>

7.2 核心概念
  1. JointPoint 可以被增强到的点,在Spring中,这些点指的是方法

  2. PointCut (切入点)指被增强的连接点(方法)

  3. Advice(通知) 具体增强的代码

  4. Target(目标对象) 被增强的对象

  5. Aspect(切面) 切入点和通知的结合

  6. Proxy(代理)一个类被AOP增强后,就产生一个结果代理类

<br/>

7.3 切点表达式

确定对哪些方法进行增强

  
  execution(* com.sangeng.service.*.*(..))   表示com.sangeng.service包下任意类,方法名任意,参数列表任意,返回值类型任意
     
  execution(* com.sangeng.service..*.*(..))   表示com.sangeng.service包及其子包下任意类,方法名任意,参数列表任意,返回值类型任意
      
  execution(* com.sangeng.service.*.*())     表示com.sangeng.service包下任意类,方法名任意,要求方法不能有参数,返回值类型任意
      
  execution(* com.sangeng.service.*.delete*(..))     表示com.sangeng.service包下任意类,要求方法不能有参数,返回值类型任意,方法名要求已delete开头
7.4 切点函数@annotation

首先自定义注解

  
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  public @interface InvokeLog {
  ​
  }

在切面类中

  
  @Component
  @Aspect
  public class MyAspect {
  ​
      @Pointcut("@annotation(com.sangeng.aspect.InvokeLog)")
      public void pt(){
          System.out.println("方法被调用了");
      }
  }

在需要增强的个方法上

  
      @InvokeLog
      public void deleteAll(){
          System.out.println("PhoneService中deleteAll的核心代码");
      }

<br/>

7.5 通知
  1. @before 前置通知 目标方法执行前

  2. @AfterReturning 返回后通知,在目标方法执行后执行,如果出现异常不会执行

  3. @After 后置通知 在目标方法执行后执行,无论是否有异常都会执行

  4. @AfterThrowing 异常通知 在目标方法抛出异常后执行

  5. @Around 环绕通知 围绕着目标方法执行 可以进行全方位的增强

<br/>

7.6 获取被增强方法的信息
  
      @Before("pt()")
      public void before(JoinPoint joinPoint){
          MethodSignature signature = (MethodSignature) joinPoint.getSignature();   // 被增强方法的方法签名
          signature.getName();  // 被增强方法的方法名
          signature.getReturnType(); // 返回值类型
          
          Method method = signature.getMethod(); // 获取方法对象
          method.invoke(); // 执行目标方法
      }

案例

  
  @Component
  @Aspect
  public class PrintLogAspect {
  ​
      //对哪些方法增强
      @Pointcut("execution(* com.sangeng.service..*.*(..))")
      public void pt(){}
  ​
      //怎么增强
      @Before("pt()")
      public void printLog(JoinPoint joinPoint){
          //输出 被增强的方法所在的类名 方法名 调用时传入的参数   joinPoint.getSignature().getName()  joinPoint.getArgs()
          MethodSignature signature = (MethodSignature) joinPoint.getSignature();
          //类名
          String className = signature.getDeclaringTypeName();
          //方法名
          String methodName = signature.getName();
          //调用时传入的参数
          Object[] args = joinPoint.getArgs();
  ​
          System.out.println(className+"=="+methodName+"======"+ Arrays.toString(args));
      }
  }

<br/>

获取异常对象和返回值返回值

  
      @AfterReturning(value = "pt()",returning = "ret") // returning属性制定了把目标方法的返回值赋值给下面方法的参数ret
      public void afterReturning(JoinPoint joinPoint,Object ret){
          System.out.println("afterReturning");
      }

异常对象

  
      @AfterThrowing(value = "pt()",throwing = "e")
      public void afterThrowing(JoinPoint joinPoint,Throwable e){
          String message = e.getMessage();
          System.out.println("afterThrowing");
      }

环绕通知获取所有信息

  
      @Around("pt()")
      public Object around(ProceedingJoinPoint pjp){
          //获取参数
          Object[] args = pjp.getArgs();
          MethodSignature signature = (MethodSignature) pjp.getSignature();  //  方法签名
          Object target = pjp.getTarget(); // 被增强的对象 
          Object ret = null;
          try {
              ret = pjp.proceed();//  相当于目标方法的执行
              //  ret就是被增强方法的返回值
              System.out.println(ret);
          } catch (Throwable throwable) {
              throwable.printStackTrace();   // 异常对象
              System.out.println(throwable.getMessage());
          }
          return ret;
      }
7.7 应用案例
  
  @Controller
  public class AIController /*implements IAIContoller*/{
      //AI自动回答
      public String getAnswer(String question){
          //AI核心代码 价值10个亿
          String str = question.replace("吗", "");
          str = str.replace("?","!");
          return str;
      }
  ​
      //AI算命
      @Crypt
      public String fortuneTelling(String name){
          System.out.println(name);
                //AI算命核心代码
          String[] strs = {"女犯伤官把夫克,旱地莲花栽不活,不是吃上两家饭,也要刷上三家锅。","一朵鲜花头上戴,一年四季也不开,一心想要花开时,采花之人没到来。","此命生来脾气暴,上来一阵双脚跳,对你脾气啥都好,经常与人吵和闹。"};
          int index = name.hashCode() % 3;
  ​
          return strs[index];
      }
  }

自定义注解

  
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface Crypt {
  ​
  ​
  }

切面类

  
  @Component
  @Aspect
  public class CryptAspect {
  ​
      //确定切点
      @Pointcut("@annotation(com.sangeng.aspect.Crypt)")
      public void pt(){
  ​
      }
  ​
  }

定义通知

  
  @Component
  @Aspect
  public class CryptAspect {
  ​
      //确定切点
      @Pointcut("@annotation(com.sangeng.aspect.Crypt)")
      public void pt(){
  ​
      }
  ​
      //定义通知
      @Around("pt()")
      public Object crypt(ProceedingJoinPoint pjp) {
          //获取去目标方法调用时的参数
          Object[] args = pjp.getArgs();
          //对参数进行解密  解密后传入目标方法执行
          String arg = (String) args[0];
          String s = CryptUtil.AESdecode(arg);//解密
          args[0] = s;
          Object proceed = null;
          String ret = null;
          try {
              proceed = pjp.proceed(args);//目标方法调用
              //目标方法执行后需要获取到返回值
              ret = (String) proceed;
              //对返回值加密后进行真正的返回
              ret = CryptUtil.AESencode(ret);
          } catch (Throwable throwable) {
              throwable.printStackTrace();
          }
          return ret;
      }
  ​
  }
7.8 多切面顺序问题

多个切面的情况,可能要控制切面的顺序注解配置 : 在切面类上加上@Order注解控制顺序xml排至 : 通过在配置文件中的配置顺序配置

注解配置

自定义注解

  
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface Crypt {
  ​
  ​
  }

切面1

  
  @Component
  @Aspect
  @Order(1)
  public class CryptAspect {
  ​
      //确定切点
      @Pointcut("@annotation(com.sangeng.aspect.Crypt)")
      public void pt(){
  ​
      }
  ​
      //定义通知
      @Around("pt()")
      public Object crypt(ProceedingJoinPoint pjp) {
          //获取去目标方法调用时的参数
          Object[] args = pjp.getArgs();
          //对参数进行解密  解密后传入目标方法执行
          String arg = (String) args[0];
          String s = CryptUtil.AESdecode(arg);//解密
          args[0] = s;
          Object proceed = null;
          String ret = null;
          try {
              proceed = pjp.proceed(args);//目标方法调用
              //目标方法执行后需要获取到返回值
              ret = (String) proceed;
              //对返回值加密后进行真正的返回
              ret = CryptUtil.AESencode(ret);
          } catch (Throwable throwable) {
              throwable.printStackTrace();
          }
          return ret;
      }
  ​
  }

切面2

  
  @Component
  @Aspect
  @Order(2)
  public class APrintLogAspect {
  ​
      //对哪些方法增强
      @Pointcut("execution(* com.sangeng.controller..*.*(..))")
      public void pt(){}
  ​
      //怎么增强
      @Before("pt()")
      public void printLog(JoinPoint joinPoint){
          //输出 被增强的方法所在的类名 方法名 调用时传入的参数   joinPoint.getSignature().getName()  joinPoint.getArgs()
          MethodSignature signature = (MethodSignature) joinPoint.getSignature();
          //类名
          String className = signature.getDeclaringTypeName();
          //方法名
          String methodName = signature.getName();
          //调用时传入的参数
          Object[] args = joinPoint.getArgs();
          System.out.println(className+"=="+methodName+"======"+ Arrays.toString(args));
      }
  }

优先级 1 > 2 > 3······

7.9 AOP原理-动态代理
JDK动态代理
  
   public static void main(String[] args) {
          AIControllerImpl aiController = new AIControllerImpl();
  //        String answer = aiController.getAnswer("三连了吗?");
  //        System.out.println(answer);
  ​
          //使用动态代理增强getAnswer方法
  ​
          //1.JDK动态代理
          //  获取类加载器
          ClassLoader cl = Demo.class.getClassLoader();
  ​
          //  被代理类所实现接口的字节码对象数组
          Class<?>[] interfaces = AIControllerImpl.class.getInterfaces();
  ​
          AIController proxy = (AIController) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
              //  使用代理对象的方法时 会调用到invoke
                  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  //  proxy   是代理对象,  method 是当前被调用的方法封装的Method对象,  args   是调用方法时传入的参数
                      
                  //  调用被代理对象的对应方法
                  //  判断 当前调用的是否是getAnswer方法
                  if(method.getName().equals("getAnswer")){
                      System.out.println("增强");
                  }
                  Object ret = method.invoke(aiController, args);
                  return ret;
              }
          });
          String answer = proxy.getAnswer("三连了吗?");
  ​
          System.out.println(proxy.fortuneTelling("张三"));
  //        System.out.println(answer);
      }

Cglib动态代理

  
      public static void main(String[] args) {
          Enhancer enhancer = new Enhancer();
          //设置父类的字节码对象
          enhancer.setSuperclass(AIControllerImpl.class);
          enhancer.setCallback(new MethodInterceptor() {
              //  使用代理对象执行方法是都会调用到intercept方法
              @Override
              public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                  //  判断当前调用的方法是不是getAnswer方法 如果是进行增强
                  if ("getAnswer".equals(method.getName())){
                      System.out.println("被增强了");
                  }
                  //调用父类中对应的方法
                  Object ret = methodProxy.invokeSuper(o, objects);
                  return ret;
              }
          });
          //生成代理对象
          AIControllerImpl proxy = (AIControllerImpl) enhancer.create();
          System.out.println(proxy.getAnswer("你好吗?"));
  //        System.out.println(proxy.fortuneTelling("你好吗?"));
      }

JDK的动态代理要求被代理的类必须实现接口,生成的代理对象相当于是被代理对象的兄弟

Cglib的动态代理不要求被代理的类要实现接口,生成的代理对象相当于被代理类的子类对象

Spring的AOP默认是JDK的动态代理,如果是用不了JDK的动态代理才会去用Cglib的动态代理

8. Spring声明式事务

8.1 注解实现声明式事务

转账案例

  
      @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
      public void transfer(Integer outId, Integer inId, Double money) {
          //增加
          accoutDao.updateMoney(inId,money);
          //减少
          accoutDao.updateMoney(outId,-money);
      }

事务传播行为 propagation

  
  propagation = Propagation.REQUIRES_NEW

外层方法有事务,内层也新建事务,外层没有事务同样新建

隔离级别ioslation

isolation = Isolation.READ_COMMITTED

只读事务

  
      @Transactional(readOnly = true)
      public void log() {
          System.out.println("打印日志");
          int i = 1/0;
      }

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值