前面的博客我们写AOP仅仅是在原始方法前后追加一些操作,接下来我们要说说AOP中数据相关的内容,我们将从获取参数获取返回值获取异常三个方面来研究切入点的相关信息。

前面我们介绍通知类型的时候总共讲了五种,那么对于这五种类型都会有参数,返回值和异常吗?

我们先来一个个分析下:

  • 获取切入点方法的参数,所有的通知类型都可以获取参数

    • JoinPoint:适用于前置、后置、返回后、抛出异常后通知

    • ProceedingJoinPoint:适用于环绕通知

  • 获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究

    • 返回后通知

    • 环绕通知

  • 获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究

    • 抛出异常后通知

    • 环绕通知

一、环境准备

  • 创建一个Maven项目

  • pom.xml添加Spring依赖

    <dependencies>
       <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>5.2.10.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.9.4</version>
       </dependency>
     </dependencies>
  • 添加BookDao和BookDaoImpl类

    public interface BookDao {
       public String findName(int id);
    }
    @Repository
    public class BookDaoImpl implements BookDao {

       public String findName(int id) {
           System.out.println("id:"+id);
           return "itcast";
      }
    }
  • 创建Spring的配置类

    @Configuration
    @ComponentScan("com.itheima")
    @EnableAspectJAutoProxy
    public class SpringConfig {
    }
  • 编写通知类

    @Component
    @Aspect
    public class MyAdvice {
       @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
       private void pt(){}

       @Before("pt()")
       public void before() {
           System.out.println("before advice ..." );
      }

       @After("pt()")
       public void after() {
           System.out.println("after advice ...");
      }

       @Around("pt()")
       public Object around() throws Throwable{
           Object ret = pjp.proceed();
           return ret;
      }
       @AfterReturning("pt()")
       public void afterReturning() {
           System.out.println("afterReturning advice ...");
      }


       @AfterThrowing("pt()")
       public void afterThrowing() {
           System.out.println("afterThrowing advice ...");
      }
    }
  • 编写App运行类

    public class App {
       public static void main(String[] args) {
           ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
           BookDao bookDao = ctx.getBean(BookDao.class);
           String name = bookDao.findName(100);
           System.out.println(name);
      }
    }

最终创建好的项目结构如下:

二、获取参数

非环绕通知获取方式

在方法上添加JoinPoint,通过JoinPoint来获取参数

@Component
@Aspect
public class MyAdvice {
   @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
   private void pt(){}

   @Before("pt()")
   public void before(JoinPoint jp)
       Object[] args = jp.getArgs();
       System.out.println(Arrays.toString(args));
       System.out.println("before advice ..." );
  }
//...其他的略
}

运行App类,可以获取如下内容,说明参数100已经被获取

思考:方法的参数只有一个,为什么获取的是一个数组?

因为参数的个数是不固定的,所以使用数组更通配些。

如果将参数改成两个会是什么效果呢?

(1)修改BookDao接口和BookDaoImpl实现类

public interface BookDao {
   public String findName(int id,String password);
}
@Repository
public class BookDaoImpl implements BookDao {

   public String findName(int id,String password) {
       System.out.println("id:"+id);
       return "itcast";
  }
}

(2)修改App类,调用方法传入多个参数

public class App {
   public static void main(String[] args) {
       ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
       BookDao bookDao = ctx.getBean(BookDao.class);
       String name = bookDao.findName(100,"itheima");
       System.out.println(name);
  }
}

(3)运行App,查看结果,说明两个参数都已经被获取到

说明:

使用JoinPoint的方式获取参数适用于前置后置返回后抛出异常后通知。

环绕通知获取方式

环绕通知使用的是ProceedingJoinPoint,因为ProceedingJoinPoint是JoinPoint类的子类,所以对于ProceedingJoinPoint类中应该也会有对应的getArgs()方法,我们去验证下:

@Component
@Aspect
public class MyAdvice {
   @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
   private void pt(){}

   @Around("pt()")
   public Object around(ProceedingJoinPoint pjp)throws Throwable {
       Object[] args = pjp.getArgs();
       System.out.println(Arrays.toString(args));
       Object ret = pjp.proceed();
       return ret;
  }
//其他的略
}

运行App后查看运行结果,说明ProceedingJoinPoint也是可以通过getArgs()获取参数

注意:

  • pjp.proceed()方法是有两个构造方法,分别是:

    • 调用无参数的proceed,当原始方法有参数,会在调用的过程中自动传入参数

    • 所以调用这两个方法的任意一个都可以完成功能

    • 但是当需要修改原始方法的参数时,就只能采用带有参数的方法,如下:

      @Component
      @Aspect
      public class MyAdvice {
         @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
         private void pt(){}

         @Around("pt()")
         public Object around(ProceedingJoinPoint pjp) throws Throwable{
             Object[] args = pjp.getArgs();
             System.out.println(Arrays.toString(args));
             args[0] = 666;
             Object ret = pjp.proceed(args);
             return ret;
        }
      //其他的略
      }

      有了这个特性后,我们就可以在环绕通知中对原始方法的参数进行拦截过滤,避免由于参数的问题导致程序无法正确运行,保证代码的健壮性。

三、获取返回值

对于返回值,只有返回后AfterReturing和环绕Around这两个通知类型可以获取,具体如何获取?

环绕通知获取返回值
@Component
@Aspect
public class MyAdvice {
   @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
   private void pt(){}

   @Around("pt()")
   public Object around(ProceedingJoinPoint pjp) throws Throwable{
       Object[] args = pjp.getArgs();
       System.out.println(Arrays.toString(args));
       args[0] = 666;
       Object ret = pjp.proceed(args);
       return ret;
  }
//其他的略
}

上述代码中,ret就是方法的返回值,我们是可以直接获取,不但可以获取,如果需要还可以进行修改。

返回后通知获取返回值
@Component
@Aspect
public class MyAdvice {
   @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
   private void pt(){}

   @AfterReturning(value = "pt()",returning = "ret")
   public void afterReturning(Object ret) {
       System.out.println("afterReturning advice ..."+ret);
  }
//其他的略
}

注意:

(1)参数名的问题

(2)afterReturning方法参数类型的问题

参数类型可以写成String,但是为了能匹配更多的参数类型,建议写成Object类型

(3)afterReturning方法参数的顺序问题

运行App后查看运行结果,说明返回值已经被获取到

四、获取异常

对于获取抛出的异常,只有抛出异常后AfterThrowing和环绕Around这两个通知类型可以获取,具体如何获取?

环绕通知获取异常

这块比较简单,以前我们是抛出异常,现在只需要将异常捕获,就可以获取到原始方法的异常信息了

@Component
@Aspect
public class MyAdvice {
   @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
   private void pt(){}

   @Around("pt()")
   public Object around(ProceedingJoinPoint pjp){
       Object[] args = pjp.getArgs();
       System.out.println(Arrays.toString(args));
       args[0] = 666;
       Object ret = null;
       try{
           ret = pjp.proceed(args);
      }catch(Throwable throwable){
           t.printStackTrace();
      }
       return ret;
  }
//其他的略
}

在catch方法中就可以获取到异常,至于获取到异常以后该如何处理,这个就和你的业务需求有关了。

抛出异常后通知获取异常
@Component
@Aspect
public class MyAdvice {
   @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
   private void pt(){}

   @AfterThrowing(value = "pt()",throwing = "t")
   public void afterThrowing(Throwable t) {
       System.out.println("afterThrowing advice ..."+t);
  }
//其他的略
}

如何让原始方法抛出异常,方式有很多,

@Repository
public class BookDaoImpl implements BookDao {

   public String findName(int id,String password) {
       System.out.println("id:"+id);
       if(true){
           throw new NullPointerException();
      }
       return "itcast";
  }
}

注意:

运行App后,查看控制台,就能看的异常信息被打印到控制台

Java开发学习(十八)----AOP通知获取数据(参数、返回值、异常)的更多相关文章

  1. Java开发学习(十六)----AOP切入点表达式及五种通知类型解析

    一.AOP切入点表达式 对于AOP中切入点表达式,总共有三个大的方面,分别是语法格式.通配符和书写技巧. 1.1 语法格式 首先我们先要明确两个概念: 切入点:要进行增强的方法 切入点表达式:要进行增 ...

  2. Java开发学习(十五)----AOP入门案例及其工作流程解析

    一.AOP简介 1.1 什么是AOP AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构. OOP(Object Oriented ...

  3. Java开发学习(十九)----AOP环绕通知案例之密码数据兼容处理

    一.需求分析 需求: 对百度网盘分享链接输入密码时尾部多输入的空格做兼容处理. 问题描述: 点击链接,会提示,请输入提取码,如下图所示 当我们从别人发给我们的内容中复制提取码的时候,有时候会多复制到一 ...

  4. Java开发学习(十)----基于注解开发定义bean 已完成

    一.环境准备 先来准备下环境: 创建一个Maven项目 pom.xml添加Spring的依赖 <dependencies>    <dependency>        < ...

  5. Java开发学习(十二)----基于注解开发依赖注入

    Spring为了使用注解简化开发,并没有提供构造函数注入.setter注入对应的注解,只提供了自动装配的注解实现. 1.环境准备 首先准备环境: 创建一个Maven项目 pom.xml添加Spring ...

  6. Java开发学习(十四)----Spring整合Mybatis及Junit

    一.Spring整合Mybatis思路分析 1.1 环境准备 步骤1:准备数据库表 Mybatis是来操作数据库表,所以先创建一个数据库及表 create database spring_db cha ...

  7. oracle学习 十二 使用.net程序调用带返回值的存储过程(持续更新)

    数据库返回的是结果集,存储过程返回的是一个或者多个值,所以不要使用while循环去读取,也不要使用datareader函数去调用.v_class_name是返回函数 使用.net调用oracle数据库 ...

  8. Java开发学习(二十八)----拦截器(Interceptor)详细解析

    一.拦截器概念 讲解拦截器的概念之前,我们先看一张图: (1)浏览器发送一个请求会先到Tomcat的web服务器 (2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源 (3)如 ...

  9. Java开发学习(三十六)----SpringBoot三种配置文件解析

    一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...

随机推荐

  1. Redis 的数据过期了就会马上删除么?

    码哥,当 key 达到过期时间,Redis 就会马上删除么? 先说结论,并不会立马删除,Redis 有两种删除过期数据的策略: 定期选取部分数据删除: 惰性删除: 该命令在 Redis 2.4 版本, ...

  2. arts-week12

    Algorithm 69. Sqrt(x) - LeetCode Review Cloudflare goes InterPlanetary - Introducing Cloudflare's IP ...

  3. MySQL8小时问题

    一.问题 获取MySQL连接,8小时内无请求自动断开连接. 二.解决 2.1 分析 MySQL服务器默认的"wait_timeout"是28800秒即8小时,意味着如果一个连接的空 ...

  4. 数据库与MySQL的下载使用

    目录 数据存储演变史 数据库应用发展史 数据库本质 数据库分类 关系型数据库 非关系型数据库 SQL与NoSQL MySQL简介 版本问题 下载使用 目录结构 基本使用 简单使用 系统服务 修改密码 ...

  5. python文件操作拓展与认识函数

    目录 文件内光标的移动(了解即可) 前言 控制光标移动seek()方法 文件的修改 函数 语法结构 简单的使用 作业 答案 文件内光标的移动(了解即可) 前言 在文件的内置方法中,read()方法是可 ...

  6. ABP框架之——数据访问基础架构

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享阅读心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. 几乎所有的业务应用程序都要适用一种数据库基础架构,用来实现数据访问逻辑,以便从数 ...

  7. 深入浅出Nginx实战与架构

    本文主要内容如下(让读者朋友们深入浅出地理解Nginx,有代码有示例有图): 1.Nginx是什么? 2.Nginx具有哪些功能? 3.Nginx的应用场景有哪些? 4.Nginx的衍生生态有哪些? ...

  8. PowerShell 定时刷新查看文件内容

    get-content .\1.txt -ReadCount 0 -Tail 5 -Wait

  9. 【Spring】AOP实现原理(三):创建代理

    AbstractAutoProxyCreator 在AbstractAutoProxyCreator的wrapIfNecessary方法中,调用getAdvicesAndAdvisorsForBean ...

  10. AspNetCore&云效Flow持续集成

    如今有了越来越多的持续集成工具,给的个人开发者的福利也是很足了,如无必要,自建工具有时只是作为练手了. 众多持续集成工具 现在可用的持续集成工具繁多,各大云服务商都推出了持续集成,甚至是一定条件内都是 ...