一、簡單介紹
Spring AOP中支持的切點指示符(PCD)有以下:execution、within、this、target、args、@target、@args、@within、@annotation和bean。
Spring 官網的介紹:
execution
: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP.(用於匹配方法執行的連接點。 這是使用Spring AOP時要使用的主要切入點指示符)
args
: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.(將匹配限制爲連接點(使用Spring AOP時方法的執行),其中參數是給定類型的實例)
通常情況下,兩者很容易使用和區分,比如:
1、表示任意public 方法的執行:
execution(public * *(..))
2、任何只有單個參數且在運行時傳遞的參數是Param實例方法的執行(注意Param要帶上包路徑):
args(com.dxc.opentalk.springtest.param.Param)
二、區分示例
官網給出一個例子,如下:
args(java.io.Serializable)
execution(**(java.io.Serializable))
這上面兩個切點表達式有什麼區別呢?如下:
Note that the pointcut given in this example is different from execution(* *(java.io.Serializable))
. The args version matches if the argument passed at runtime is Serializable
, and the execution version matches if the method signature declares a single parameter of type Serializable
.
個人理解args(java.io.Serializable)匹配的是隻有單個入參而且入參是可以序列化(實現Serializable接口或者Serializable的子接口)的方法;而execution(* *(java.io.Serializable))
匹配的是隻有單個入參而且入參聲明的必須是Serializable接口(不能是該接口的實現類以及子接口,嚴格的參數類型匹配)
三 、代碼驗證
定義一個類A,其三個方法爲連接點:
@Component
public class A {
public void testOne(Param param) {
System.out.println("testOne exec..." + param.getName());
}
public void testTwo(java.io.Serializable serializable) {
System.out.println("testTwo exec..." + serializable.toString());
}
public void testThree(com.dxc.opentalk.springtest.service.TestInterface testInterface) {
System.out.println("testThree exec..." + testInterface.toString());
}
}
定義切面、切點以及通知:
@Aspect
@Component
public class AopConfig {
@Pointcut("args(java.io.Serializable)")
private void apply(){}
@Before("apply()")
public void check(){
System.out.println("aop args()...");
}
}
定義一個繼承java.io.Serializable接口的TestInterface子接口,一個參數類Param 實現java.io.Serializable接口,一個ParamOne類實現TestInterface子接口:
public interface TestInterface extends Serializable {
void test();
}
public class Param implements Serializable {
private static final long serialVersionUID = 1342018181486589136L;
private String name;
public Param(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class ParamOne implements TestInterface {
private static final long serialVersionUID = 3558001836605280932L;
public void test() {
System.out.println("extended interface exec...");
}
private String name;
public ParamOne(String name){
this.name = name;
}
public String getName(){
return name;
}
}
主程序類:
@EnableAspectJAutoProxy
@ComponentScan("com.dxc.opentalk.springtest")
public class BootStrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(BootStrap.class);
A a = (A) applicationContext.getBean("a");
a.testOne(new Param("param"));
a.testTwo(new ArrayList());
a.testThree(new ParamOne("paramOne"));
}
}
當採用 @Pointcut("args(java.io.Serializable)") 時,程序輸出結果:
aop args()...
testOne exec...param
aop args()...
testTwo exec...[]
aop args()...
testThree exec...com.dxc.opentalk.springtest.param.ParamOne@68267da0
可以看到,A類中的三個連接點方法全部被切點匹配上了。
當採用@Pointcut("execution(* *(java.io.Serializable))")時,程序輸出結果:
testOne exec...param
aop args()...
testTwo exec...[]
testThree exec...com.dxc.opentalk.springtest.param.ParamOne@6ab778a
可以看到,只有入參聲明是Serializable 的第二個方法void testTwo(java.io.Serializable serializable)被切點匹配上。
四、小結
execution表達式的標準格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
綜上,execution表達式中的param-pattern是嚴格的類型匹配(必須是方法聲明的參數類型,子類也不可以);args表達式參數是給定類型的實例即可匹配到。