遗留问题:还存在一个问题,我们可以通过ProceedingJoinPoint 执行切入点函数,那么我们在环绕通知中可以获取到数据吗?
答案是可以的,下面我们来一起探究一下:
AOP切入点数据获取
1.1 获取参数
说明:在前置/后置通知和环绕通知中都可以获取到连接点方法的参数们,注意是 们 也就是说我们获取到的是个参数数组,累死主函数的args
系统给我们提供了两种方式:
JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
@Before("pt()")
public void before(JoinPoint jp) {
System.out.println("before advice 参数..." );
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
@After("pt()")
public void after(JoinPoint jp) {
System.out.println("after advice 参数...");
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("around before advice 参数...");
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("around after advice...");
return ret;
}
ps:小技巧,我们也可以使用around对参数数值进行处理,例如下面修改参数值,然后需要在调用原函数时候将参数传进去
例如下面用法
//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("around before advice 参数...");
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
// 修改参数值
args[0] = 666;
Object ret = null;
try {
ret = pjp.proceed(args);
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("around after advice...");
return ret;
}
1.2 获取返回值
说明:在返回后通知和环绕通知中都可以获取到连接点方法的返回值
- 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(String ret) {
// 变量名要和returning="ret"的属性值一致
System.out.println("afterReturning advice ..."+ret);
}
- 环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 手动调用连接点方法,返回值就是连接点方法的返回值
Object ret = pjp.proceed();
System.out.println("around after advice 返回值...");
return ret;
}
两种方式运行结果如下:
1.3 获取异常
说明:在抛出异常后通知和环绕通知中都可以获取到连接点方法中出现的异常
- 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
//变量名要和throwing = "t"的属性值一致
System.out.println("afterThrowing advice ..."+ t);
}
- 抛出异常后通知可以获取切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
Object ret = null;
//此处需要try...catch处理,catch中捕获到的异常就是连接点方法中抛出的异常
try {
ret = pjp.proceed();
} catch (Throwable t) {
System.out.println("around advice 捕捉异常...");
t.printStackTrace();
}
return ret;
}
例如运行结果如下:
代码案例
项目结构如下:
导入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yyl</groupId>
<artifactId>spring_20_aop_advice_type</artifactId>
<version>1.0-SNAPSHOT</version>
<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>
</project>
编写配置类,开启切面编程配置
package com.yyl.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.yyl")
@EnableAspectJAutoProxy
public class SpringConfig {
}
编写接口与实现类
package com.yyl.dao;
public interface BookDao {
public String findName(int id,String password);
}
package com.yyl.dao.impl;
import com.yyl.dao.BookDao;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao {
public String findName(int id, String password) {
System.out.println("id:" + id);
if (false){
throw new NullPointerException();
}
return "yyl";
}
}
编写通知类,我们使用5种通知类型 分别配置 update与save方法上
package com.yyl.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.yyl.dao.BookDao.findName(..))")
private void pt(){
}
//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
@Before("pt()")
public void before(JoinPoint jp) {
System.out.println("before advice 参数..." );
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
@After("pt()")
public void after(JoinPoint jp) {
System.out.println("after advice 参数...");
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("around before advice 参数...");
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = null;
try {
ret = pjp.proceed(args);
} catch (Throwable t) {
System.out.println("around advice 捕捉异常...");
t.printStackTrace();
}
System.out.println("around after advice 返回值...");
return ret;
}
//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp,String ret) {
System.out.println("afterReturning advice 返回值..."+ret);
}
//设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
}
}
运行结果如下:
我们在findName中加一个异常,再运行
获取到了异常信息