Spring之路(44)–Spring AOP通知类型详解与实例展示

简介: 本文目录1. 通知是干啥的2. 前置通知演示3. 正常返回通知演示4. 异常返回通知演示5. 返回通知演示6. 环绕通知演示7. 总结

1. 通知是干啥的

上一篇我们演示了一种通知,即使用@Before标识的在接入点执行的方法。通知就是切面要执行的特定行为。


实际上通知很灵活,还有其他种类的通知,具体如下:


注解 名称 说明

前置通知 @Before 在实际方法调用之前调用被注解的通知方法

正常返回通知 @AfterReturning 实际方法执行完毕后执行该通知,注意抛出异常则不会执行该通知

异常返回通知 @AfterThrowing 实际执行方法抛出异常执行该通知

返回通知 @After 实际方法调用之后执行该通知,不论是否发生异常

环绕通知 @Around 方法执行之前和之后都可以执行通知指定动作,这个比较强大

2. 前置通知演示

还是车辆出门前登记这个场景,注意通知参数可以携带JoinPoint参数,该参数中包含被通知的方法信息、还有目标对象的信息,这样便于我们操作。


第一,货车类和轿车类,提供容器中bean的类型信息。


package org.maoge.aopdemo.useaop;

/**

* 货车

*/

public class Truck {

public void out() {

 System.out.println("卡车出门");

}

public void in() {

 System.out.println("卡车进门");

}

}


package org.maoge.aopdemo.useaop;

/**

* 轿车

*/

public class Car {

public void out() {

 System.out.println("轿车出门");

}

public void in() {

 System.out.println("轿车进门");

}

}


第二,在配置类中将类型注册为bean,同时开启AOP,开启指定包的bean扫描。


package org.maoge.aopdemo.useaop;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**

* 配置类

*/

@Configuration // 配置类,用来配置容器

@EnableAspectJAutoProxy // 开启AOP

@ComponentScan(basePackages = { "org.maoge.aopdemo.useaop" }) // 扫描包以便发现注解配置的bean

public class SpringConfig {

@Bean // 注册卡车bean

public Truck Truck() {

 Truck truck = new Truck();

 return truck;

}

@Bean // 注册轿车bean

public Car car() {

 Car car = new Car();

 return car;

}

}


第三,配置切面,并编写前置通知


package org.maoge.aopdemo.useaop;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.springframework.stereotype.Component;

/**

* 车辆出门切面

*/

@Component // 切面也是Spring的bean

@Aspect // 使用该注解标志此类是一切面

public class OutAspect {

  // 前置通知

  @Before("execution(public void out())")//在public void out()方法之前执行通知

  public void outNote(JoinPoint joinPoint) {

   System.out.println("出门登记信息");

   System.out.println("joinPoint.signature:" + joinPoint.getSignature());// 接入方法信息

   System.out.println("joinPoint.target.class.name:" + joinPoint.getTarget().getClass().getName());// 接入目标对象的类型信息

  }

}


第四,测试类


package org.maoge.aopdemo.useaop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

  public static void main(String[] args) {

   // 获取容器

   AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

   // 取出bean

   Truck truck = (Truck) context.getBean(Truck.class);

   // 执行bean方法

   truck.out();

   Car car = (Car) context.getBean(Car.class);

   car.out();

  }

}


执行结果如下,可见前置通知执行成功,且已输出接入点方法信息和目标对象信息。


出门登记信息

joinPoint.kind:method-execution

joinPoint.signature:void org.maoge.aopdemo.useaop.Truck.out()

joinPoint.target.class.name:org.maoge.aopdemo.useaop.Truck

卡车出门

出门登记信息

joinPoint.kind:method-execution

joinPoint.signature:void org.maoge.aopdemo.useaop.Car.out()

joinPoint.target.class.name:org.maoge.aopdemo.useaop.Car

轿车出门


3. 正常返回通知演示

直接在切面类中添加通知即可,非常简单,注意如果抛出异常,该通知是不执行的。


  // 正常返回通知

  @AfterReturning("execution(public void out())")

  public void AfterReturning(JoinPoint joinPoint) {

   System.out.println("车辆已出门");

  }


4. 异常返回通知演示

在切面中定义异常返回通知,如下:


// 异常返回通知

@AfterThrowing("execution(public void out())")

public void afterThrowing(JoinPoint joinPoint) {

 System.out.println(joinPoint.getSignature()+"发生异常");// 接入方法信息

}


注意必须抛出才能执行该通知,如果方法内部处理了异常,则不会执行异常返回通知,如下:


package org.maoge.aopdemo.useaop;

/**

* 轿车

*/

public class Car {

//抛出异常,会执行异常通知

public void out() {

 System.out.println("轿车出门");

 int a=1/0;

}

public void in() {

 System.out.println("轿车进门");

}

}


package org.maoge.aopdemo.useaop;

/**

* 货车

*/

public class Truck {

//已处理异常,不会执行异常通知

public void out() {

 System.out.println("卡车出门");

 try {

  int a=1/0;

 }catch(Exception e) {

 }

}

public void in() {

 System.out.println("卡车进门");

}

}


5. 返回通知演示

返回通知是不管是否发生异常,都会执行的,示例如下:


// 返回通知(不论是否有异常都会执行)

  @After("execution(public void out())")

  public void after(JoinPoint joinPoint) {

   System.out.println("车辆出门这个事我知道了");// 接入方法信息

  }


6. 环绕通知演示

环绕通知能够在目标方法执行之前、之后启动,如果要记录一个方法的执行时间,那么使用环绕通知是很合适的,如下:


// 环绕通知,记录方法执行时间

  @Around("execution(public void out())")

  public void around(ProceedingJoinPoint joinPoint) throws Throwable {

   long startTime = System.currentTimeMillis();//开始时间

   joinPoint.proceed();//这一行代码表示执行目标方法

   System.out.println(joinPoint.getSignature() + "运行时间(毫秒)为:" + (System.currentTimeMillis() - startTime));

  }


我们来看下输出:


出门登记信息

joinPoint.signature:void org.maoge.aopdemo.useaop.Truck.out()

joinPoint.target.class.name:org.maoge.aopdemo.useaop.Truck

卡车出门

void org.maoge.aopdemo.useaop.Truck.out()运行时间(毫秒)为:7

车辆出门这个事我知道了

车辆已出门

出门登记信息

joinPoint.signature:void org.maoge.aopdemo.useaop.Car.out()

joinPoint.target.class.name:org.maoge.aopdemo.useaop.Car

轿车出门

车辆出门这个事我知道了

void org.maoge.aopdemo.useaop.Car.out()发生异常

Exception in thread "main" java.lang.ArithmeticException: / by zero


可见没有异常的时候,统计运行时间成功了,当发生异常时,环绕通知中抛出异常,未能执行到打印运行时间那一行,所以改为:


// 环绕通知,记录方法执行时间

  @Around("execution(public void out())")

  public void around(ProceedingJoinPoint joinPoint) {

   long startTime = System.currentTimeMillis();// 开始时间

   try {

    joinPoint.proceed();// 这一行代码表示执行目标方法

   } catch (Throwable e) {

    e.printStackTrace();

   }

   System.out.println(joinPoint.getSignature() + "运行时间(毫秒)为:" + (System.currentTimeMillis() - startTime));

  }


7. 总结

看到这里,想必大家也能深深体会AOP的强大和作用了,例如我们完全可以针对一些指定的方法启用事务,利用环绕通知在方法开始前开启事务,在方法执行后提交事务,发生异常时回滚。


Spring AOP是功能封装的利器,当项目中越来越多的使用到AOP时,说明已逐渐从初级的Java工程师向中级进阶啦,恭喜!

相关文章
|
3天前
|
前端开发 Java 数据库
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
|
3天前
|
XML Java 数据格式
技术好文:Spring基础篇——AOP切面编程
技术好文:Spring基础篇——AOP切面编程
|
2天前
|
Java 开发者 Spring
使用Spring Boot AOP实现日志记录
使用Spring Boot AOP实现日志记录
|
2天前
|
XML Java 数据格式
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
5 0
|
3天前
|
XML Java API
经验大分享:Spring实现AOP的三种方式
经验大分享:Spring实现AOP的三种方式
|
3天前
|
XML Java API
经验大分享:Spring实现AOP的三种方式
经验大分享:Spring实现AOP的三种方式
|
3天前
|
XML Java 开发者
“掌握Spring IoC和AOP:30道面试必备问题解析!“
“掌握Spring IoC和AOP:30道面试必备问题解析!“
10 0
|
4天前
|
缓存 算法 Java
spring-三级缓存-生命周期-spring事务-IOC-AOP
spring-三级缓存-生命周期-spring事务-IOC-AOP
|
5天前
|
XML 监控 Java
Java中的AOP编程:AspectJ与Spring AOP的应用
Java中的AOP编程:AspectJ与Spring AOP的应用
|
9天前
|
Java Maven Spring
Spring中AOP最简单实例-@注解形式
Spring中AOP最简单实例-@注解形式
17 0