Spring boot AOP 之 @Aspect 注解使用
AOP相关术语
-
切面(Aspect):是指横切多个对象的关注点的一个模块化,事务管理就是J2EE应用中横切关注点的很好示例。在Spring AOP中,切面通过常规类(基本模式方法)或者通过使用了注解@Aspect的常规类来实现。
-
连接点(Joint point):是指在程序执行期间的一个点,比如某个方法的执行或者是某个异常的处理。在Spring AOP中,一个连接点往往代表的是一个方法执行。
-
通知(Advice):是指切面在某个特殊连接点上执行的动作。通知有不同类型,包括"around","before"和"after"通知。许多AOP框架包括Spring,将通知建模成一个拦截器,并且围绕连接点维持一个拦截器链。
-
切入点(Pointcut):是指匹配连接点的一个断言。通知是和一个切入点表达式关联的,并且在任何被切入点匹配的连接点上运行(举例,使用特定的名字执行某个方法)。AOP的核心就是切入点表达式匹配连接点的思想。Spring默认使用AspectJ切入点表达式语言
-
引入(Introduction):代表了对一个类型额外的方法或者属性的声明。Spring AOP允许引入新接口到任何被通知对象(以及一个对应实现)。比如,可以使用一个引入去使一个bean实现IsModified接口,从而简化缓存机制。(在AspectJ社区中,一个引入也称为一个inter-type declaration类型间声明) 目标对象(Target object):是指被一个或多个切面通知的那个对象。也指被通知对象("advised object"),由于Spring AOP是通过运行时代理事项的,这个目标对象往往是一个代理对象。
-
AOP 代理(AOP proxy):是指通过AOP框架创建的对象,用来实现切面合约的(执行通知方法等等)。在Spring框架中,一个AOP代理是一个JDK动态代理或者是一个CGLIB代理。
-
织入(Weaving):将切面和其他应用类型或者对象连接起来,创骗一个被通知对象。这些可以在编译时(如使用AspectJ编译器)、加载时或者运行时完成。Spring AOP,比如其他纯Java AOP框架一般是在运行时完成织入。
AOP Advice相关术语
-
前置通知(Before advice):在一个连接点之前执行的通知。但这种通知不能阻止连接点的执行流程(除非它抛出一个异常)
-
后置返回通知(After returning advice):在一个连接点正常完成后执行的通知(如,如果一个方法没有抛出异常的返回)
-
后置异常通知(After throwing advice):在一个方法抛出一个异常退出时执行的通知。
-
后置(最终)通知(After(finally) advice):在一个连接点退出时(不管是正常还是异常返回)执行的通知。
-
环绕通知(Around advice):环绕一个连接点的通知,比如方法的调用。这是一个最强大的通知类型。环绕通知可以在方法调用之前和之后完成自定义的行为。也负责通过返回自己的返回值或者抛出异常这些方式,选择是否继续执行连接点或者简化被通知方法的执行。
spring boot aop jar 包引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Spring AOP 在通用方法执行切面逻辑 代码示例
- 定义切面
@Aspect
@Component
public class LogAspect1 {
// com.pangugle.testspringboot.controller 包中所有的类的所有方法切面
// @Pointcut("execution(public * com.pangugle.testspringboot.controller.*.*(..))")
// 只针对 HelloController 类切面
// @Pointcut("execution(public *
// com.pangugle.testspringboot.controller.HelloController.*(..))")
// 统一切点,对com.pangugle.testspringboot.controller及其子包中所有的类的所有方法切面
//@Pointcut("execution(public * com.pangugle.testspringboot.controller..*.*(..))")
@Pointcut("execution(* com.pangugle.testspringboot.service.*.*(..))")
public void pointcut() {}
@Before("pointcut()")
public void printParam(JoinPoint joinPoint){
//获取请求的方法
Signature sig = joinPoint.getSignature();
String method = joinPoint.getTarget().getClass().getName() + "." + sig.getName();
//获取请求的参数
Object[] args = joinPoint.getArgs();
//fastjson转换
String params = FastJsonHelper.jsonEncode(args);
//打印请求参数
System.out.println(method + " : " + params);
}
}
切点作用范围
-
通过通配符
@Pointcut("execution(public * com.pangugle.testspringboot.controller.*.*(..))")
-
可通过通配符,利用 ..
@Pointcut("execution(public * com.pangugle.testspringboot.controller..*.*(..))") 和上面的区别是 ..
-
只针对 特定类切面、 特定方法
@Pointcut("execution(public * com.pangugle.testspringboot.controller.HelloController.*(..))")
代码解释
-
我们在 LogAspect1 类上 添加了 @Aspect 注解, @Component 注解
@Aspect 注解 在这里表示 LogAspect1 被定义为 aop的一个切面 @Component 注解 表示 让spring 来管理 LogAspect1
-
@Pointcut("execution(* com.pangugle.testspringboot.service..(..))")
1. @Pointcut 注解在 pointcut 方法上, 表明 poincut 为一个切点 2. execution(* com.pangugle.testspringboot.service.*.*(..)) 这个表示 在 com.pangugle.testspringboot.service 中的所有类都执行这个切点
-
@Before("pointcut()")
1. @Before("pointcut()") 注解在 printParam 方法上 表示 在 com.pangugle.testspringboot.service 中的所有类 方法执行之前先 执行 printParam() 这个方法, 这个也叫 前置通知!
注意:
在使用过程中 com.pangugle.testspringboot.service 这个包,请替换成你们自己的包名,或着具体的类!
Spring AOP 在特定方法执行切面逻辑 代码示例
这个方式要 自定义 注解
- 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
定义一个 MyLog 注解
- 定义切面
@Aspect
@Component
public class LogAspect2 {
// 针对特定方法
@Pointcut("@annotation(myLog)")
public void myPointcut(MyLog myLog) {
}
// @Around:环绕通知
@Around("myPointcut(myLog)")
public Object Around(ProceedingJoinPoint pjp, MyLog myLog) throws Throwable {
System.out.println("around执行方法之前");
Object object = pjp.proceed();
System.out.println("around执行方法之后--返回值:" + object);
return object;
}
// 前置通知
@Before("myPointcut(myLog)")
public void beforeMethod(JoinPoint joinPoint, MyLog myLog) {
System.out.println("调用了前置通知");
}
// @AfterThrowing: 异常通知
@AfterThrowing(value = "myPointcut(myLog)", throwing = "e")
public void afterReturningMethod(JoinPoint joinPoint, MyLog myLog, Exception e) {
System.out.println("调用了异常通知");
}
// @After: 后置通知
@After("myPointcut(myLog)")
public void afterMethod(JoinPoint joinPoint, MyLog myLog) {
System.out.println("调用了后置通知");
}
// @AfterRunning: 返回通知 rsult为返回内容
@AfterReturning(value = "myPointcut(myLog)", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result, MyLog myLog) {
System.out.println("调用了返回通知");
}
}
代码解释
-
@Aspect 和 @Component 的作用 和 Spring AOP 在通用方法执行切面逻辑 代码示例 一样,这里不重复解释
-
@Pointcut("@annotation(myLog)")
@Pointcut("@annotation(myLog)") 这个注解在 myPointcut 方法上: 表示 myPointcut 方法是一个切点,并且有一个参数,这个要注意,在添加切面逻辑的时候一定带上这个参数
-
剩下的就是切面各个生命周期的具体切点逻辑了
这里一定要注意:
注解带参数,名称一定要对应起来,
我们在上面 定义切点带上 参数 myLog, 所以各个切点逻辑要带上 MyLog myLog 这个参数!
这次说明,不要忘记参数! 不要忘记参数! 不要忘记参数!