Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。就是抽离出来的逻辑类,比如日志、权限等。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。定义需要拦截的函数。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target(目标对象):织入 Advice 的目标对象.。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
after return advice, 在一个 join point 正常返回后执行的 advice
after throwing advice, 当一个 join point 抛出异常后执行的 advice
after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
introduction,introduction可以为原有的对象增加新的属性和方法。
使用 execution 指定一个切点,就是定义需要拦截的东西。
1.定义一个切面
package org.example.springbootaop.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// 切面
@Aspect
@Component
public class AopAdvice {
// 定义切点
@Pointcut("execution(* org.example.springbootaop.controller.*.*(..))")
public void test() {}
// 通知:advice
@Before("test()")
public void beforeAdvice() {
System.out.println("before advice...");
}
@After("test()")
public void afterAdvice() {
System.out.println("afterAdvice...");
}
@AfterReturning("test()")
public void afterReturn() {
System.out.println("afterReturn....");
}
@Around("test()")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("beforce");
try {
proceedingJoinPoint.proceed();
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("after");
}
}
execution(* org.example.springbootaop.controller.*.*(…))
第一个*代表方法返回值类型,第二个*代表任意类,第三个*代表任意方法,(…)代表任意参数类型。
1.自定义一个注解
package org.example.springbootaop.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionsAnnotation {
}
2.定义一个切面
package org.example.springbootaop.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CustomAnnAopAdvice {
// 指定注解所在的包
@Pointcut("@annotation(org.example.springbootaop.annotaion.PermissionsAnnotation)")
private void permissionLog(){}
@Before("permissionLog()")
public void beforeAdvice() {
System.out.println("custom annotation aop before");
}
}
3.在对应的函数上使用注解
package org.example.springbootaop.controller;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.example.springbootaop.annotaion.PermissionsAnnotation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AnnotationAopController {
@PostMapping("/aopTest")
public String aopTest() {
return "annotation aop";
}
@RequestMapping("/customAop")
@PermissionsAnnotation // 只用自定义的注解
public String customAop() {
return "custom annotation aop";
}
}
使用以存在的注解,比如 GetMapping 也是这样使用,只不过 PointCut里指定GetMapping的注解包路径。
完整的代码: https://github.com/chenguowei/project_java/blob/main/spring-boot-examples-demo/spring-boot-aop/README.md