前置,后置,返回后,异常通知
Spring AOP 五种通知类型
Spring AOP 一共提供5种类型的通知:
before 前置通知, 在目标方法运行前, 先去执行 切面类 的 切面方法
after 后置通知,在目标方法运行后,再执行切面类的切面方法
after return 后置返回通知,在目标方法返回数据以后,再执行切面类的切面方法
after throwing 异常通知, 在目标方法抛出异常后, 再执行切面类的切面方法
around 环绕通知,最为强around 环绕通知,最为强大,可自定义通知执行时机,可决定目标方法是否运行
其中 after 和 after return 本质上没有区别,其执行的先后顺序由 applicationContext 中的 标签顺序决定
详解前四种通知类型
切面类中的 printExecutionTime, doAfter, doAfterReturning, doAfterThrowing 方法用来分别演示 前置, 后置,后置返回, 异常通知类型。
//切面类
public class MethodAspect {
//切面方法, 用于快速扩展额外功能
//JointPoint, 连接点, 通过连接点可以获取目标方法的信息
public void printExecutionTime(JoinPoint joinPoint){
SimpleDateFormat sdt= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSSS");
String now = sdt.format(new Date());
String className = joinPoint.getTarget().getClass().getName();//获取目标类的名称
String methodName = joinPoint.getSignature().getName();//获取目标方法的名称
System.out.println(now +" : "+className+"."+methodName);
Object[] args = joinPoint.getArgs();//获取目标方法参数
System.out.println("----> 参数个数" + args.length );
for (Object arg :args){
System.out.println("--->参数 : "+ arg);
}
}
// 后置通知方法,在目标方法运行后,再执行切面类的切面方法
public void doAfter(JoinPoint joinPoint){
System.out.println("触发后置通知");
}
// 后置返回通知方法, 在目标方法返回数据后,再执行切面类的切面方法
// ret 接受到的目标方法返回值
public void doAfterReturning(JoinPoint joinPoint, Object ret){
System.out.println("返回后置通知 :"+ret);
}
// 异常通知方法, 在目标方法抛出异常后, 再执行切面类的切面方法
// th 接受到的异常
public void doAfterThrowing(JoinPoint joinPoint, Throwable th){
System.out.println("异常通知 :"+ th);
System.out.println("异常通知 :"+ th);
}
}
在 applicationContext.xml 文件中添加四种通知标签: <aop: before>, <aop: after>, <aop: after-returning>, <aop: after-throwing>。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDao" class="indi.chester.spring.aop.dao.UserDao"/>
<bean id="userService" class="indi.chester.spring.aop.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<!-- 切面类 -->
<bean id="methodAspect" class="indi.chester.spring.aop.aspect.MethodAspect"/>
<!-- 配置 AOP -->
<aop:config>
<!-- pointcut 切点, 使用 execution 表达式描述切面的作用范围 -->
<!-- pointcut 切点表达式: pubilc 访问修饰符, * 任意返回类型, .. 包通配符, *.* 任意类的任意方法, (..) 参数通配符 -->
<!-- 作用于 indi.chester 包下所有public类的运行 -->
<aop:pointcut id="pointcut" expression="execution(public * indi.chester..*.*(..))"/>
<!-- 定义切面类 -->
<aop:aspect ref="methodAspect">
<!-- before 前置通知, 在目标方法运行前, 先去执行 切面类 的 切面方法-->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
<!-- after 后置通知,在目标方法运行后,再执行切面类的切面方法,after和after-returning 的运行顺序是根据配置文件的先后顺序-->
<aop:after method="doAfter" pointcut-ref="pointcut"/>
<!-- after-returning 后置返回通知,在目标方法返回数据以后,再执行切面类的切面方法,after和after-returning 的运行顺序是根据配置文件的先后顺序-->
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
<!-- after-throwing 异常通知, 在目标方法抛出异常后, 再执行切面类的切面方法,after和after-throwing 的运行顺序是根据配置文件的先后顺序-->
<aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试一下:
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser();
userService.generateRandomPassword("MD5", 16);
}
}
运行结果:
2022-02-18 22:47:38 0495 : indi.chester.spring.aop.service.UserService.createUser
----> 参数个数0
执行创建用户业务逻辑
2022-02-18 22:47:38 0513 : indi.chester.spring.aop.dao.UserDao.insert
----> 参数个数0
新增用户数据
触发后置通知
返回后置通知 :null
触发后置通知
返回后置通知 :null
2022-02-18 22:47:38 0521 : indi.chester.spring.aop.service.UserService.generateRandomPassword
----> 参数个数2
--->参数 : MD5
--->参数 : 16
按MD5方式生成16位随机密码
触发后置通知
返回后置通知 :Zxcquei1
为了创造出一个异常,需要修改 UserService 类中的 createUser 方法
public class UserService {
private UserDao userDao;
public void createUser(){
//为了演示 <aop:after-throwing>
if(1==1){
throw new RuntimeException("用户已存在");
}
System.out.println("执行创建用户业务逻辑");
userDao.insert();
}
public String generateRandomPassword(String type , Integer length){
System.out.println("按" + type + "方式生成"+ length + "位随机密码");
return "Zxcquei1";
}
// getter setter 方法省略
}
再次运行, 发现捕获到了异常:
2022-02-18 22:49:10 0757 : indi.chester.spring.aop.service.UserService.createUser
----> 参数个数0
触发后置通知
异常通知 :java.lang.RuntimeException: 用户已存在
Exception in thread "main" java.lang.RuntimeException: 用户已存在
at indi.chester.spring.aop.service.UserService.createUser(UserService.java:11)
at indi.chester.spring.aop.service.UserService$$FastClassBySpringCGLIB$$af5d8448.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at indi.chester.spring.aop.service.UserService$$EnhancerBySpringCGLIB$$caa6fbaf.createUser(<generated>)
at indi.chester.spring.aop.SpringApplication.main(SpringApplication.java:11)
Last updated