Pointcut表達式類型
標准的AspectJ Aop的pointcut的表達式類型是很豐富的,但是Spring Aop只支持其中的9種,外加Spring Aop自己擴充的一種一共是11(10+1)
種類型的表達式,分別如下。
- execution:一般用於指定方法的執行,用的最多。
- within:指定某些類型的全部方法執行,也可用來指定一個包。
- this:Spring Aop是基於動態代理的,生成的bean也是一個代理對象,this就是這個代理對象,當這個對象可以轉換為指定的類型時,對應的切入點就是它了,Spring Aop將生效。
- target:當被代理的對象可以轉換為指定的類型時,對應的切入點就是它了,Spring Aop將生效。
- args:當執行的方法的參數是指定類型時生效。
- @target:當代理的目標對象上擁有指定的注解時生效。
- @args:當執行的方法參數類型上擁有指定的注解時生效。
- @within:與@target類似,看官方文檔和網上的說法都是@within只需要目標對象的類或者父類上有指定的注解,則@within會生效,而@target則是必須是目標對象的類上有指定的注解。而根據筆者的測試這兩者都是只要目標類或父類上有指定的注解即可。
- @annotation:當執行的方法上擁有指定的注解時生效。
reference pointcut
:(經常使用)表示引用其他命名切入點,只有@ApectJ風格支持,Schema風格不支持bean:當調用的方法是指定的bean的方法時生效。(Spring AOP自己擴展支持的)
Pointcut定義時,還可以使用&&、||、! 這三個運算。進行邏輯運算。可以把各種條件組合起來使用
AspectJ切入點支持的切入點指示符還有:call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode
;但Spring AOP目前不支持這些指示符,使用這些指示符將拋出IllegalArgumentException異常。這些指示符Spring AOP可能
會在以后進行擴展
aspectj支持的所有切點表達式類型如下(但Spring目前只支持如上)
見org.aspectj.weaver.tools.PointcutPrimitive
這個枚舉類:
// 相當於AspectJ一共提供了24中之多(當然不包含Spring自己的bean的模式) public final class PointcutPrimitive extends TypeSafeEnum { public static final PointcutPrimitive CALL = new PointcutPrimitive("call",1); public static final PointcutPrimitive EXECUTION = new PointcutPrimitive("execution",2); public static final PointcutPrimitive GET = new PointcutPrimitive("get",3); public static final PointcutPrimitive SET = new PointcutPrimitive("set",4); public static final PointcutPrimitive INITIALIZATION = new PointcutPrimitive("initialization",5); public static final PointcutPrimitive PRE_INITIALIZATION = new PointcutPrimitive("preinitialization",6); public static final PointcutPrimitive STATIC_INITIALIZATION = new PointcutPrimitive("staticinitialization",7); public static final PointcutPrimitive HANDLER = new PointcutPrimitive("handler",8); public static final PointcutPrimitive ADVICE_EXECUTION = new PointcutPrimitive("adviceexecution",9); public static final PointcutPrimitive WITHIN = new PointcutPrimitive("within",10); public static final PointcutPrimitive WITHIN_CODE = new PointcutPrimitive("withincode",11); public static final PointcutPrimitive CFLOW = new PointcutPrimitive("cflow",12); public static final PointcutPrimitive CFLOW_BELOW = new PointcutPrimitive("cflowbelow",13); public static final PointcutPrimitive IF = new PointcutPrimitive("if",14); public static final PointcutPrimitive THIS = new PointcutPrimitive("this",15); public static final PointcutPrimitive TARGET = new PointcutPrimitive("target",16); public static final PointcutPrimitive ARGS = new PointcutPrimitive("args",17); public static final PointcutPrimitive REFERENCE = new PointcutPrimitive("reference pointcut",18); public static final PointcutPrimitive AT_ANNOTATION = new PointcutPrimitive("@annotation",19); public static final PointcutPrimitive AT_THIS = new PointcutPrimitive("@this",20); public static final PointcutPrimitive AT_TARGET = new PointcutPrimitive("@target",21); public static final PointcutPrimitive AT_ARGS = new PointcutPrimitive("@args",22); public static final PointcutPrimitive AT_WITHIN = new PointcutPrimitive("@within",23); public static final PointcutPrimitive AT_WITHINCODE = new PointcutPrimitive("@withincode",24); private PointcutPrimitive(String name, int key) { super(name, key); } }
使用示例
execution:
execution是使用的最多的一種Pointcut表達式,表示某個方法的執行,其標准語法如下。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
- 修飾符匹配(modifier-pattern?)
- 返回值匹配(ret-type-pattern)可以為*表示任何返回值,全路徑的類名等
- 類路徑匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
- 參數匹配((param-pattern))可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“”來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(,String) 表示匹配有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(…)表示零個或多個任意參數
- 異常類型匹配(throws-pattern?)
- 其中后面跟着“?”的是可選項
下面看幾個例子:
//表示匹配所有方法 1)execution(* *(..)) //表示匹配com.fsx.run.UserService中所有的公有方法 2)execution(public * com.fsx.run.UserService.*(..)) //表示匹配com.fsx.run包及其子包下的所有方法 3)execution(* com.fsx.run..*.*(..))
Pointcut定義時,還可以使用&&、||、! 這三個運算。進行邏輯運算
// 簽名:消息發送切面 @Pointcut("execution(* com.fsx.run.MessageSender.*(..))") private void logSender(){} // 簽名:消息接收切面 @Pointcut("execution(* com.fsx.run.MessageReceiver.*(..))") private void logReceiver(){} // 只有滿足發送 或者 接收 這個切面都會切進去 @Pointcut("logSender() || logReceiver()") private void logMessage(){}
這個例子中,logMessage()將匹配任何MessageSender和MessageReceiver中的任何方法。
當我們的切面很多的時候,我們可以把所有的切面放到單獨的一個類去,進行統一管理,比如下面:
//集中管理所有的切入點表達式 public class Pointcuts { @Pointcut("execution(* *Message(..))") public void logMessage(){} @Pointcut("execution(* *Attachment(..))") public void logAttachment(){} @Pointcut("execution(* *Service.*(..))") public void auth(){} }
這樣別的使用時,采用全類名+方法名的方式
@Before("com.fsx.run.Pointcuts.logMessage()") public void before(JoinPoint joinPoint) { System.out.println("Logging before " + joinPoint.getSignature().getName()); }
within:
within是用來指定類型的,指定類型中的所有方法將被攔截。
// AService下面所有外部調用方法,都會攔截。備注:只能是AService的方法,子類不會攔截的 @Pointcut("within(com.fsx.run.service.AService)") public void pointCut() { }
所以此處需要注意:上面寫的是AService接口,是達不到攔截效果的,只能寫實現類:
//此處只能寫實現類 @Pointcut("within(com.fsx.run.service.impl.AServiceImpl)") public void pointCut() { }
匹配包以及子包內的所有類:
@Pointcut("within(com.fsx.run.service..*)") public void pointCut() { }
this:
Spring Aop是基於代理的,this就表示代理對象。this類型的Pointcut表達式的語法是this(type),當生成的代理對象可以轉換為type指定的類型時則表示匹配。基於JDK接口的代理和基於CGLIB的代理生成的代理對象是不一樣的。(注意和上面within的區別)
// 這樣子,就可以攔截到AService所有的子類的所有外部調用方法 @Pointcut("this(com.fsx.run.service.AService*)") public void pointCut() { }
target:
Spring Aop是基於代理的,target則表示被代理的目標對象。當被代理的目標對象可以被轉換為指定的類型時則表示匹配。 注意:和上面不一樣,這里是target,因此如果要切入,只能寫實現類了
@Pointcut("target(com.fsx.run.service.impl.AServiceImpl)") public void pointCut() { }
args:
args用來匹配方法參數的。
- 1、“args()”匹配任何不帶參數的方法。
- 2、“args(java.lang.String)”匹配任何只帶一個參數,而且這個參數的類型是String的方法。
- 3、“args(…)”帶任意參數的方法。
- 4、“args(java.lang.String,…)”匹配帶任意個參數,但是第一個參數的類型是String的方法。
- 5、“args(…,java.lang.String)”匹配帶任意個參數,但是最后一個參數的類型是String的方法。
@Pointcut("args()") public void pointCut() { }
這個匹配的范圍非常廣,所以一般和別的表達式結合起來使用
@target:
@target匹配當被代理的目標對象對應的類型及其父類型上擁有指定的注解時。
//能夠切入類上(非方法上)標准了MyAnno注解的所有外部調用方法 @Pointcut("@target(com.fsx.run.anno.MyAnno)") public void pointCut() { }
@args:
@args匹配被調用的方法上含有參數,且對應的參數類型上擁有指定的注解的情況。 例如:
// 匹配**方法參數類型上**擁有MyAnno注解的方法調用。
//如我們有一個方法add(MyParam param)接收一個MyParam類型的參數,而MyParam這個類是擁有注解MyAnno的,則它可以被Pointcut表達式匹配上 @Pointcut("@args(com.fsx.run.anno.MyAnno)") public void pointCut() { }
@within:
@within用於匹配被代理的目標對象對應的類型或其父類型擁有指定的注解的情況,但只有在調用擁有指定注解的類上的方法時才匹配。
“@within(com.fsx.run.anno.MyAnno)”匹配被調用的方法聲明的類上擁有MyAnno注解的情況。比如有一個ClassA上使用了注解MyAnno標注,並且定義了一個方法a(),那么在調用ClassA.a()方法時將匹配該Pointcut;如果有一個ClassB上沒有MyAnno注解,但是它繼承自ClassA,同時它上面定義了一個方法b(),那么在調用ClassB().b()方法時不會匹配該Pointcut,但是在調用ClassB().a()時將匹配該方法調用,因為a()是定義在父類型ClassA上的,且ClassA上使用了MyAnno注解。但是如果子類ClassB覆寫了父類ClassA的a()方法,則調用ClassB.a()方法時也不匹配該Pointcut。
@annotation:使用得也比較多
@annotation用於匹配方法上擁有指定注解的情況。
// 可以匹配所有方法上標有此注解的方法 @Pointcut("@annotation(com.fsx.run.anno.MyAnno)") public void pointCut() { }
我們還可以這么寫,非常方便的獲取到方法上面的注解
@Before("@annotation(myAnno)") public void doBefore(JoinPoint joinPoint, MyAnno myAnno) { System.out.println(myAnno); //@com.fsx.run.anno.MyAnno() System.out.println("AOP Before Advice..."); }
reference pointcut:切入點引用(使用得非常多)
@Aspect public class HelloAspect { @Pointcut("execution(* com.fsx.service.*.*(..)) ") public void point() { } // 這個就是一個`reference pointcut` 甚至還可以這樣 @Before("point1() && point2()") @Before("point()") public void before() { System.out.println("this is from HelloAspect#before..."); } }
bean: 這是Spring增加的一種方法,spring獨有
bean用於匹配當調用的是指定的Spring的某個bean的方法時。
1、“bean(abc)”匹配Spring Bean容器中id或name為abc的bean的方法調用。
2、“bean(user*)”匹配所有id或name為以user開頭的bean的方法調用。
// 這個就能切入到AServiceImpl類的素有的外部調用的方法里 @Pointcut("bean(AServiceImpl)") public void pointCut() { }
類型匹配語法
*:匹配任何數量字符;
…:匹配任何數量字符的重復,如在類型模式中匹配任何數量子包;而在方法參數模式中匹配任何數量參數。
+:匹配指定類型的子類型;僅能作為后綴放在類型模式后邊。
java.lang.String 匹配String類型; java.*.String 匹配java包下的任何“一級子包”下的String類型; 如匹配java.lang.String,但不匹配java.lang.ss.String java..* 匹配java包及任何子包下的任何類型。如匹配java.lang.String、java.lang.annotation.Annotation java.lang.*ing 匹配任何java.lang包下的以ing結尾的類型; java.lang.Number+ 匹配java.lang包下的任何Number的子類型; 如匹配java.lang.Integer,也匹配java.math.BigInteger
表達式的組合
表達式的組合其實就是對應的表達式的邏輯運算,與、或、非。可以通過它們把多個表達式組合在一起。
1、“bean(userService) && args()”匹配id或name為userService的bean的所有無參方法。
2、“bean(userService) || @annotation(MyAnnotation)”匹配id或name為userService的bean的方法調用,或者是方法上使用了MyAnnotation注解的方法調用。
3、“bean(userService) && !args()”匹配id或name為userService的bean的所有有參方法調用。
Ref:(35條消息) @Pointcut()的execution、@annotation等參數說明_java_green_hand0909的博客-CSDN博客
【小家Spring】Spring AOP中@Pointcut切入點表達式最全面使用介紹 - 雲+社區 - 騰訊雲 (tencent.com)