AOP(Aspect Oriented Programming,面向切面编程)是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在Spring AOP中业务逻辑仅仅只关注业务本身,将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,从而在改变这些行为的时候不影响业务逻辑的代码。
相关注解介绍:
注解 | 作用 |
@Aspect | 把当前类标识为一个切面 |
@Pointcut | Pointcut是织入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。 |
@Around | 环绕增强,目标方法执行前后分别执行一些代码 |
@AfterReturning | 返回增强,目标方法正常执行完毕时执行 |
@Before | 前置增强,目标方法执行之前执行 |
@AfterThrowing | 异常抛出增强,目标方法发生异常的时候执行 |
@After | 后置增强,不管是抛出异常或者正常退出都会执行 |
一、添加依赖
<dependency>
<groupId></groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、编写增强类
package ;
import ;
import .slf4j.Slf4j;
import ;
import .*;
import ;
@Aspect
@Component
@Slf4j
public class LogAspect {
/**
* 前置增强:目标方法执行之前执行
*
* @param jp
*/
@Before("execution(* .*.*(..))") // 所有controller包下面的所有方法的所有参数
public void beforeMethod(JoinPoint jp) {
String methodName = ().getName();
("【前置增强】the method 【" + methodName + "】 begins with " + (()));
}
/**
* 后置增强:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
*
* @param jp
*/
@After("execution(* .*.*(..)))")
public void afterMethod(JoinPoint jp) {
("【后置增强】this is a afterMethod advice...");
}
/**
* 返回增强:目标方法正常执行完毕时执行
*
* @param jp
* @param result
*/
@AfterReturning(value = "execution(* .*.*(..)))", returning = "result")
public void afterReturningMethod(JoinPoint jp, Object result) {
String methodName = ().getName();
("【返回增强】the method 【" + methodName + "】 ends with 【" + result + "】");
}
/**
* 异常增强:目标方法发生异常的时候执行,第二个参数表示补货异常的类型
*
* @param jp
* @param e
*/
@AfterThrowing(value = "execution(* .*.*(..))", throwing = "e")
public void afterThorwingMethod(JoinPoint jp, Exception e) {
String methodName = ().getName();
("【异常增强】the method 【" + methodName + "】 occurs exception: ", e);
}
/**
* 环绕增强:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
*
* @return
*/
/* @Around(value = "execution(* .*.*(..))")
public Object aroundMethod(ProceedingJoinPoint jp) {
String methodName = ().getName();
Object result = null;
try {
("【环绕增强中的--->前置增强】:the method 【" + methodName + "】 begins with " + (()));
//执行目标方法
result = ();
("【环绕增强中的--->返回增强】:the method 【" + methodName + "】 ends with " + result);
} catch (Throwable e) {
result = "error";
("【环绕增强中的--->异常增强】:the method 【" + methodName + "】 occurs exception " + e);
}
("【环绕增强中的--->后置增强】:-----------------end.----------------------");
return result;
}*/
}
三、Controller
package ;
import ;
import .slf4j.Slf4j;
import ;
import ;
@RestController
@Slf4j
public class TestController {
@GetMapping("/doNormal")
public String doNormal(String name, String age, User user) {
("【执行方法】:doNormal");
return "doNormal";
}
@GetMapping("/doWithException")
public String doWithException(String name, String age, User user) {
("【执行方法】:doWithException");
int a = 1 / 0;
return "doWithException";
}
}
启动程序,当访问doNormal方法时,日志输出如下:
2019-05-13 14:36:20.931 INFO 12592 --- [nio-9379-exec-5] : 【前置通知】the method 【doNormal】 begins with ["zhangsan","12",{"address":["chengdu","beijing"],"name":"zhangsan"}]
2019-05-13 14:36:20.932 INFO 12592 --- [nio-9379-exec-5] : 【执行方法】:doNormal
2019-05-13 14:36:20.932 INFO 12592 --- [nio-9379-exec-5] : 【后置通知】this is a afterMethod advice...
2019-05-13 14:36:20.932 INFO 12592 --- [nio-9379-exec-5] : 【返回通知】the method 【doNormal】 ends with 【doNormal】
当访问doWithException方法时,日志输出如下:
2019-05-13 14:45:03.699 INFO 12592 --- [nio-9379-exec-8] : 【前置通知】the method 【doWithException】 begins with ["zhangsan","12",{"address":["chengdu","beijing"],"name":"zhangsan"}]
2019-05-13 14:45:03.699 INFO 12592 --- [nio-9379-exec-8] : 【执行方法】:doWithException
2019-05-13 14:45:03.699 INFO 12592 --- [nio-9379-exec-8] : 【后置通知】this is a afterMethod advice...
2019-05-13 14:45:03.704 ERROR 12592 --- [nio-9379-exec-8] : 【异常通知】the method 【doWithException】 occurs exception:
: / by zero