项目概况

AOP 使用指南

2026-01-294 min read后端技术
Spring Framework

一、常用注解

注解说明
@Before前置通知, 在方法执行之前执行
@After后置通知, 在方法执行之后执行
@AfterRunning返回通知 在方法返回结果之后执行
@AfterThrowing异常通知在方法抛出异常之后
@Around环绕通知, 围绕着方法执行

二、切面表达式

注解说明
within拦截指定类及指定包下所有的类
@within拦截被指定注解修饰的类
this拦截指定的类型
args拦截指定参数类型的方法
@annotation拦截带指定注解的方法
@args拦截方法入参被中@args指定的注解(入参只能有一个)
execution表达式详情见下文

三、API使用案例

3.1 within

a. API说明

  1. 精确匹配类名
  2. 模糊匹配包中所有的类
  3. 模糊匹配包中所有的带Impl后缀的

b. 目录

text
1└── WithinMatchProcessor 2 ├── AopWithinMatchProcessor.java 3 ├── CokeImpl.java 4 ├── Water.java 5 └── readme.md

c. 拦截代码

java
1@Aspect 2@Component 3public class AopWithinMatchProcessor { 4 5 /** 6 * 精确匹配类名 7 */ 8 @Pointcut("within(spring.learning.aop.WithinMatchProcessor.Water)") 9 private void matchClassName() { 10 } 11 12 /** 13 * 模糊匹配包中所有的类 14 */ 15 @Pointcut("within(spring.learning.aop.WithinMatchProcessor.*)") 16 private void matchAllClassFromPackage() { 17 } 18 19 /** 20 * 模糊匹配包中所有的带Impl后缀的 21 */ 22 @Pointcut("within(spring.learning.aop.WithinMatchProcessor.*Impl)") 23 private void matchClassFromPackage() { 24 } 25 26 27 @Before("matchClassName()") 28 public void beforeMatchClassName() { 29 System.out.println("--------精确匹配类名-------"); 30 } 31 32 @Before("matchAllClassFromPackage()") 33 public void beforeMatchAllClassFormPackage() { 34 System.out.println("--------模糊匹配包中所有的类-------"); 35 } 36 37 @Before("matchClassFromPackage()") 38 public void beforeMatchClassFromPackage() { 39 System.out.println("--------模糊匹配包中所有的带Impl后缀的-------"); 40 } 41 42 43} 44

3.2 @within

a. API说明

拦截被指定注解标注的类

b. 目录

text
1├── AnnotationWithinMatchProcessor 2│ ├── AopAnnotationWithinMatchProcessor.java 3│ ├── Log.java 4│ ├── Sprite.java 5│ └── readme.md 6

c. 拦截代码

java
1@Log(tag = "SpriteLog") 2@Component 3public class Sprite { 4 5 public void drink() { 6 System.out.println("空参数"); 7 } 8 9 public void drink(Integer age) { 10 System.out.println("age"); 11 } 12 13 14 public String name() { 15 return "Sprite.name"; 16 } 17 18 public void toCalculate() throws Exception { 19 System.out.println(0 / 0); 20 } 21} 22 23@Aspect 24@Component 25public class AopAnnotationWithinMatchProcessor { 26 27 28 /** 29 * 注意可以将注解,放到参数中,此时@within()会将参数入参名去找到注解的类型 30 * 凡是被Log标记的类,都会被拦截 31 * 32 * @param spriteLog 注解 33 */ 34 @Before("@within(spriteLog)") 35 public void beforeAnnotationMatch(Log spriteLog) { 36 System.out.println("--------拦截被Log修饰类的所有方法" + spriteLog.tag() + "-------"); 37 } 38 39 40 /** 41 * 返回值 42 * 43 * @param value 返回值 44 * @param spriteLog 注解 45 */ 46 @AfterReturning(value = "@within(spriteLog)", returning = "value") 47 public void afterReturningAnnotationMatch(String value, Log spriteLog) { 48 System.out.println("afterReturningAnnotationMatch返回值:" + value + ",注解:" + spriteLog); 49 } 50 51 /** 52 * 拦截异常 53 * 54 * @param e 异常 55 * @param spriteLog 拦截日志 56 */ 57 @AfterThrowing(value = "@within(spriteLog)", throwing = "e") 58 public void AfterThrowingAnnotationMatch(Exception e, Log spriteLog) { 59 System.out.println(e.getMessage()); 60 } 61 62}

3.3 this

a. API说明

拦截指定的类

b. 目录

text
1├── ThisMatchProcessor 2│ ├── AopThisMatchProcessor.java 3│ ├── ThisPerson.java 4│ └── readme.md 5

c. 拦截代码

java
1@Aspect 2@Component 3public class AopThisMatchProcessor { 4 5 @Before(value = "this(ThisPerson)") 6 public void thisMatch() { 7 System.out.println("--------------ThisPerson------------"); 8 } 9} 10

3.4 args

a. API说明

java
1@Component 2public class Person { 3 4 public String info(String name) { 5 return "姓名:" + name; 6 } 7 8 public String info(String name, Integer age) { 9 return "姓名:" + name + ",年龄:" + age; 10 } 11}

Person类中有两个info方法,但是入参不一样,假如要拦截指定入参的方法时候,就可以使用args

b. 目录

text
1├── ArgsMatchProcessor 2│ ├── AopArgsMatchProcessor.java 3│ ├── Person.java 4│ └── readme.md

c. 拦截代码

可以看到args 和 within可以通过&&来进行,联合匹配。另外可以通过returning方法指定方法的返回值。但是注意,类型要和要拦截的方法的返回类型匹配。否则会报错。

java
1@Aspect 2@Component 3public class AopArgsMatchProcessor { 4 5 @AfterReturning(value = "within(Person) && args(name,age)", returning = "value") 6 public void beforeArgs(Integer age, String name, String value) { 7 System.out.println("拦截器逻辑----------------------------"); 8 System.out.println("入参name:" + name); 9 System.out.println("入参age:" + age); 10 System.out.println("返回值:" + value); 11 System.out.println("拦截器逻辑----------------------------"); 12 } 13}

3.5 @annotation

a. API说明

拦截被指定注解标记的方法。

b. 目录

text
1├── AnnotationMethodMatchProcessor 2│ ├── AopAnnotationMethodMatchProcessor.java 3│ ├── LogMethod.java 4│ └── Main.java 5

c. 代码

java
1@Aspect 2@Component 3public class AopAnnotationMethodMatchProcessor { 4 5 6 @Before(value = "@annotation(logMethod) && args(args)") 7 public void annotationMethodMatch(LogMethod logMethod, String args) { 8 System.out.println("注解方法匹配"); 9 } 10}

3.6 @args

a. API说明

拦截方法中入参被@args指定注解的方法。

b. 目录

text
1├── AnnotationArgsMatchProcessor 2│ ├── AopAnnotationArgsMatchProcessor.java 3│ ├── Apple.java 4│ ├── Fruit.java 5│ ├── Orange.java 6│ └── Teacher.java

c. 代码

注意当出现以下异常说明aop声明的拦截范围太广泛了,导致了一些不能拦截的类被拦截从而报错了,此时只用缩小拦截的范围即可

text
1 Cannot subclass final class org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages

缩小拦截范围如下使用this拦截指定类型

java
1@Aspect 2@Component 3public class AopAnnotationArgsMatchProcessor { 4 5 @Before(value = "@args(fruit) && this(Teacher)") 6 public void annotationMethodMatch(Fruit fruit) { 7 System.out.println("拦截被Fruit+tag:"+fruit.tag()); 8 } 9} 10

3.7 execution

a. API说明

execution()是最常用的切点函数,其语法如下所示:

execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?) 除了返回类型模式、方法名模式和参数模式外,其它项都是可选的

表达式说明
execution(public * *(..))匹配所有目标类的public方法
execution(* *Test(..))匹配目标类所有以To为后缀的方法
execution(spring.learning.Water.(..))匹配Water接口所有方法
execution(spring.learning.Water+.(..))匹配Water接口以及实现类中所有方法(包括Water接口中没有的方法)
execution(* spring.learning.*(..))匹配spring.learning包下所有的类所有方法
execution(* spring.learning..*(..))匹配spring.learning包及其子孙包下所有的类所有方法
execution(* spring..*.Dao.find(..))匹配包名前缀为spring的任何包下类名后缀为Dao的方法,方法名必须以find为前缀
execution(* info(String,Integer))匹配info方法中,第一个参数是String,第二个Integer的方法
execution(* info(String,*)))匹配info方法中,第一个参数是String,第二个任意类型
execution(* info(String,..)))匹配info方法中,第一个参数是String,后面任意参数
execution(* info(Object+)))匹配info方法中,方法拥有一个入参,且入参是Object类型或该类的子类