@Pointcut
用來標注在方法上來定義切入點。
使用格式:@ 注解(value=“表達標簽 (表達式格式)”)
。如:@Pointcut("execution(* com.javacode2018.aop.demo9.test1.Service1.*(..))")
表達式標簽(10種)
-
execution
:用於匹配方法執行的連接點 -
within
:用於匹配指定類型內的方法執行 -
this
:用於匹配當前AOP代理對象類型的執行方法;注意是AOP代理對象的類型匹配,這樣就可能包括引入接口類型匹配 -
target
:用於匹配當前目標對象類型的執行方法;注意是目標對象的類型匹配,這樣就不包括引入接口類型匹配 -
args
:用於匹配當前執行的方法傳入的參數為指定類型的執行方法 -
@within
:用於匹配所以持有指定注解類型內的方法 -
@target
:用於匹配當前目標對象類型的執行方法,其中目標對象持有指定的注解 -
@args
:用於匹配當前執行的方法傳入的參數持有指定注解的執行 -
@annotation
:用於匹配當前執行方法持有指定注解的方法 -
bean
:Spring AOP擴展的,AspectJ沒有對於指示符,用於匹配特定名稱的Bean對象的執行方法
10種標簽組成了12種用法
execution
使用execution(方法表達式)
匹配方法執行。
execution格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
其中帶 ?號的 modifiers-pattern?
,declaring-type-pattern?
,hrows-pattern?
是可選項
- ret-type-pattern, name-pattern, parameters-pattern是必選項
- modifier-pattern? 修飾符匹配,如public 表示匹配公有方法
- ret-type-pattern 返回值匹配,* 表示任何返回值,全路徑的類名等
- declaring-type-pattern? 類路徑匹配
- name-pattern 方法名匹配,* 代表所有,set*,代表以set開頭的所有方法
- (param-pattern) 參數匹配,指定方法參數(聲明的類型),(..)代表所有參數,(*,String)代表第一個參數為任何值,第二個為String類型,(..,String)代表最后一個參數是String類型
- throws-pattern? 異常類型匹配
舉例說明
類型匹配語法
很多地方會按照類型的匹配,先來說一下類型匹配的語法。
首先讓我們來了解下AspectJ類型匹配的通配符:
*
:匹配任何數量字符
..
:匹配任何數量字符的重復,如在類型模式中匹配任何數量子包;而在方法參數模式中匹配任何數量參數(0個或者多個參數)
+
:匹配指定類型及其子類型;僅能作為后綴放在類型模式后邊
within
用法
within(類型表達式)
:目標對象target的類型是否和within中指定的類型匹配
匹配原則
target.getClass().equals(within表達式中指定的類型)
案例
有2個類,父子關系
父類C1
package com.javacode2018.aop.demo9.test2;
public class C1 {
public void m1() {
System.out.println("我是m1");
}
public void m2() {
System.out.println("我是m2");
}
}
子類C2
package com.javacode2018.aop.demo9.test2;
public class C2 extends C1 {
@Override
public void m2() {
super.m2();
}
public void m3() {
System.out.println("我是m3");
}
}
來個Aspect類
package com.javacode2018.aop.demo9.test2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest2 {
@Pointcut("within(C1)") //@1
public void pc() {
}
@Before("pc()") //@2
public void beforeAdvice(JoinPoint joinpoint) {
System.out.println(joinpoint);
}
}
注意@1匹配的類型是C1,也就是說被代理的對象的類型必須是C1類型的才行,需要和C1完全匹配。下面我們對C2創建代理
@Test
public void test2(){
C2 target = new C2();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest2.class);
C2 proxy = proxyFactory.getProxy();
proxy.m1();
proxy.m2();
proxy.m3();
}
運行輸出
我是m1
我是m2
我是m3
原因是目標對象是C2類型的,C2雖然是C1的子類,但是within中表達式指定的是要求類型必須是C1類型的才匹配。
如果將within表達式修改為下面任意一種就可以匹配了
@Pointcut("within(C1+)")
@Pointcut("within(C2)")
再次運行輸出
execution(void com.javacode2018.aop.demo9.test2.C1.m1())
我是m1
execution(void com.javacode2018.aop.demo9.test2.C2.m2())
我是m2
execution(void com.javacode2018.aop.demo9.test2.C2.m3())
我是m3
this
用法
this(類型全限定名)
:通過aop創建的代理對象的類型是否和this中指定的類型匹配;
-
注意判斷的目標是代理對象;
-
this中使用的表達式必須是類型全限定名,不支持通配符。
匹配原則
如:this(x),則代理對象proxy滿足下面條件時會匹配 `x.getClass().isAssignableFrom(proxy.getClass());`
## 案例
來個接口
```text
package com.javacode2018.aop.demo9.test3;
public interface I1 {
void m1();
}
來個實現類
package com.javacode2018.aop.demo9.test3;
public class Service3 implements I1 {
@Override
public void m1() {
System.out.println("我是m1");
}
}
來個@Aspect類
package com.javacode2018.aop.demo9.test3;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest3 {
//@1:匹配proxy是Service3類型的所有方法
@Pointcut("this(Service3)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinpoint) {
System.out.println(joinpoint);
}
}
測試代碼
@Test
public void test3() {
Service3 target = new Service3();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
//獲取目標對象上的接口列表
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
//設置需要代理的接口
proxyFactory.setInterfaces(allInterfaces);
proxyFactory.addAspect(AspectTest3.class);
//獲取代理對象
Object proxy = proxyFactory.getProxy();
//調用代理對象的方法
((I1) proxy).m1();
System.out.println("proxy是否是jdk動態代理對象:" + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("proxy是否是cglib代理對象:" + AopUtils.isCglibProxy(proxy));
//判斷代理對象是否是Service3類型的
System.out.println(Service3.class.isAssignableFrom(proxy.getClass()));
}
運行輸出
我是m1
proxy是否是jdk動態代理對象:true
proxy是否是cglib代理對象:false
false
從輸出中可以看出m1方法沒有被增強,原因:this表達式要求代理對象必須是Service3類型的,輸出中可以看出代理對象並不是Service3類型的,此處代理對象proxy是使用jdk動態代理生成的。
我們可以將代碼調整一下,使用cglib來創建代理
proxyFactory.setProxyTargetClass(true);
再次運行,會發現m2被攔截了,結果如下
execution(void com.javacode2018.aop.demo9.test3.Service3.m1())
我是m1
proxy是否是jdk動態代理對象:false
proxy是否是cglib代理對象:true
true
target
用法
target(類型全限定名)
:判斷目標對象的類型是否和指定的類型匹配;注意判斷的是目標對象的類型;表達式必須是類型全限定名,不支持通配符。
匹配原則
如: target(x)
,則目標對象target滿足下面條件時會匹配 x.getClass().isAssignableFrom(target.getClass());
案例
package com.javacode2018.aop.demo9.test4;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest4 {
//@1:目標類型必須是Service3類型的
@Pointcut("target(com.javacode2018.aop.demo9.test3.Service3)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinpoint) {
System.out.println(joinpoint);
}
}
測試代碼
@Test
public void test4() {
Service3 target = new Service3();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setProxyTargetClass(true);
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest4.class);
//獲取代理對象
Object proxy = proxyFactory.getProxy();
//調用代理對象的方法
((I1) proxy).m1();
//判斷target對象是否是Service3類型的
System.out.println(Service3.class.isAssignableFrom(target.getClass()));
}
運行輸出
execution(void com.javacode2018.aop.demo9.test3.Service3.m1())
我是m1
true
within、this、target對比
args
用法
args(參數類型列表)
匹配當前執行的方法傳入的參數是否為args中指定的類型;
- 注意是匹配傳入的參數類型,不是匹配方法簽名的參數類型;
- 參數類型列表中的參數必須是類型全限定名,不支持通配符;
- args屬於動態切入點,也就是執行方法的時候進行判斷的,這種切入點開銷非常大,非特殊情況最好不要使用。
案例
下面的m1方法參數是Object類型的。
package com.javacode2018.aop.demo9.test5;
public class Service5 {
public void m1(Object object) {
System.out.println("我是m1方法,參數:" + object);
}
}
Aspect類
package com.javacode2018.aop.demo9.test5;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import java.util.Arrays;
import java.util.stream.Collectors;
@Aspect
public class AspectTest5 {
//@1:匹配只有1個參數其類型是String類型的
@Pointcut("args(String)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinpoint) {
System.out.println("請求參數:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
}
}
測試代碼,調用2次m1方法,第一次傳入一個String類型的,第二次傳入一個int類型的,看看效果
@Test
public void test5() {
Service5 target = new Service5();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest5.class);
Service5 proxy = proxyFactory.getProxy();
proxy.m1("路人");
proxy.m1(100);
}
運行輸出
請求參數:[路人]
我是m1方法,參數:路人
我是m1方法,參數:100
輸出中可以看出,m1第一次調用被增強了,第二次沒有被增強。
args會在調用的過程中對參數實際的類型進行匹配,比較耗時,慎用。
@within
用法
@within(注解類型)
:匹配指定的注解內定義的方法。
匹配規則
調用目標方法的時候,通過java中Method.getDeclaringClass()
獲取當前的方法是哪個類中定義的,然后會看這個類上是否有指定的注解。
被調用的目標方法Method 對象.getDeclaringClass().getAnnotation(within中指定的注解類型) != null
來看3個案例。
案例1
目標對象上有@within中指定的注解,這種情況時,目標對象的所有方法都會被攔截。
來個注解
package com.javacode2018.aop.demo9.test9;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Ann9 {
}
來個目標類,用@Ann9標注
package com.javacode2018.aop.demo9.test9;
@Ann9
public class S9 {
public void m1() {
System.out.println("我是m1方法");
}
}
來個Aspect類
package com.javacode2018.aop.demo9.test9;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest9 {
/**
* 定義目標方法的類上有Ann9注解
*/
@Pointcut("@within(Ann9)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
測試代碼
@Test
public void test9() {
S9 target = new S9();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest9.class);
S9 proxy = proxyFactory.getProxy();
proxy.m1();
}
m1方法在類S9中定義的,S9上面有Ann9注解,所以匹配成功
運行輸出
execution(void com.javacode2018.aop.demo9.test9.S9.m1())
我是m1方法
案例2
定義注解時未使用@Inherited
,說明子類無法繼承父類上的注解,這個案例中我們將定義一個這樣的注解,將注解放在目標類的父類上,來看一下效果。
定義注解Ann10
package com.javacode2018.aop.demo9.test10;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Ann10 {
}
來2個父子類
注意:S10Parent為父類,並且使用了Anno10注解,內部定義了2個方法大家注意一下,而S10位代理的目標類,繼承了S10Parent,內部重寫了父類的m2方法,並且又新增了一個m3方法
package com.javacode2018.aop.demo9.test10;
@Ann10
class S10Parent {
public void m1() {
System.out.println("我是S10Parent.m1()方法");
}
public void m2() {
System.out.println("我是S10Parent.m2()方法");
}
}
public class S10 extends S10Parent {
@Override
public void m2() {
System.out.println("我是S10.m2()方法");
}
public void m3() {
System.out.println("我是S10.m3()方法");
}
}
來個Aspect類
package com.javacode2018.aop.demo9.test10;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest10 {
//匹配目標方法聲明的類上有@Anno10注解
@Pointcut("@within(com.javacode2018.aop.demo9.test10.Ann10)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
測試用例
S10為目標類,依次執行代理對象的m1、m2、m3方法,最終會調用目標類target中對應的方法。
@Test
public void test10() {
S10 target = new S10();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest10.class);
S10 proxy = proxyFactory.getProxy();
proxy.m1();
proxy.m2();
proxy.m3();
}
運行輸出
execution(void com.javacode2018.aop.demo9.test10.S10Parent.m1())
我是S10Parent.m1()方法
我是S10.m2()方法
我是S10.m3()方法
分析結果
從輸出中可以看出,只有m1方法被攔截了,其他2個方法沒有被攔截。
確實是這樣的,m1方法的是由S10Parent定義的,這個類上面有Ann10注解。
而m2方法雖然也在S10Parent中定義了,但是這個方法被子類S10重寫了,所以調用目標對象中的m2方法的時候,此時發現m2方法是由S10定義的,而S10.class.getAnnotation(Ann10.class)
為空,所以這個方法不會被攔截。
同樣m3方法也是S10中定義的,也不會被攔截。
案例3
對案例2進行改造,在注解的定義上面加上@Inherited,此時子類可以繼承父類的注解,此時3個方法都會被攔截了。
下面上代碼,下面代碼為案例2代碼的一個拷貝,不同地方只是注解的定義上多了@Inherited
定義注解Ann11
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Ann11 {
}
2個父子類
package com.javacode2018.aop.demo9.test11;
@Ann11
class S11Parent {
public void m1() {
System.out.println("我是S11Parent.m1()方法");
}
public void m2() {
System.out.println("我是S11Parent.m2()方法");
}
}
public class S11 extends S11Parent {
@Override
public void m2() {
System.out.println("我是S11.m2()方法");
}
public void m3() {
System.out.println("我是S11.m3()方法");
}
}
Aspect類
package com.javacode2018.aop.demo9.test11;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest11 {
@Pointcut("@within(com.javacode2018.aop.demo9.test11.Ann11)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
測試用例
@Test
public void test11() {
S11 target = new S11();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest11.class);
S11 proxy = proxyFactory.getProxy();
proxy.m1();
proxy.m2();
proxy.m3();
}
運行輸出
execution(void com.javacode2018.aop.demo9.test11.S11Parent.m1())
我是S11Parent.m1()方法
execution(void com.javacode2018.aop.demo9.test11.S11.m2())
我是S11.m2()方法
execution(void com.javacode2018.aop.demo9.test11.S11.m3())
我是S11.m3()方法
這次3個方法都被攔截了。
@target
用法
@target(注解類型)
:判斷目標對象target類型上是否有指定的注解;
@target
中注解類型也必須是全限定類型名。
匹配規則
target.class.getAnnotation(指定的注解類型) != null
2種情況可以匹配
-
注解直接標注在目標類上
-
注解標注在父類上,但是注解必須是可以繼承的,即定義注解的時候,需要使用
@Inherited
標注
案例1
注解直接標注在目標類上,這種情況目標類會被匹配到。
自定義一個注解Ann6
package com.javacode2018.aop.demo9.test6;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Ann6 {
}
目標類S6
上直接使用@Ann1
package com.javacode2018.aop.demo9.test6;
@Ann6
public class S6 {
public void m1() {
System.out.println("我是m1");
}
}
來個Aspect
類
package com.javacode2018.aop.demo9.test6;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest6 {
//@1:目標類上有@Ann1注解
@Pointcut("@target(Ann1)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
測試代碼
@Test
public void test6() {
S6 target = new S6();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest6.class);
S6 proxy = proxyFactory.getProxy();
proxy.m1();
System.out.println("目標類上是否有 @Ann6 注解:" + (target.getClass().getAnnotation(Ann6.class) != null));
}
運行輸出
execution(void com.javacode2018.aop.demo9.test6.S6.m1())
我是m1
目標類上是否有 @Ann6 注解:true
案例2
注解標注在父類上,注解上沒有@Inherited
,這種情況下,目標類無法匹配到,下面看代碼
注解 Ann7
package com.javacode2018.aop.demo9.test7;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Ann7 {
}
來2個父子類,父類上有@Ann7
,之類S7
為目標類
package com.javacode2018.aop.demo9.test7;
import java.lang.annotation.Target;
@Ann7
class S7Parent {
}
public class S7 extends S7Parent {
public void m1() {
System.out.println("我是m1");
}
public static void main(String[] args) {
System.out.println(S7.class.getAnnotation(Target.class));
}
}
來個Aspect類
package com.javacode2018.aop.demo9.test7;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest7 {
/**
* 匹配目標類上有Ann7注解
*/
@Pointcut("@target(com.javacode2018.aop.demo9.test7.Ann7)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
測試代碼
@Test
public void test7() {
S7 target = new S7();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest7.class);
S7 proxy = proxyFactory.getProxy();
proxy.m1();
System.out.println("目標類上是否有 @Ann7 注解:" + (target.getClass().getAnnotation(Ann7.class) != null));
}
運行輸出
我是m1
目標類上是否有 @Ann7 注解:false
分析結果
@Ann7
標注在了父類上,但是@Ann7
定義的時候沒有使用@Inherited
,說明之類無法繼承父類上面的注解,所以上面的目標類沒有被攔截,下面我們將@Ann7
的定義改一下,加上@Inherited
package com.javacode2018.aop.demo9.test7;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Ann7 {
}
再次運行輸出
execution(void com.javacode2018.aop.demo9.test7.S7.m1())
我是m1
目標類上是否有 @Ann7 注解:true
此時目標對象被攔截了。
@args
用法
@args(注解類型)
:方法參數所屬的類上有指定的注解;
注意不是參數上有指定的注解,而是參數類型的類上有指定的注解。
案例1
@Pointcut("@args(Ann8)")
:匹配方法只有一個參數,並且參數所屬的類上有Ann8注解
可以匹配下面的代碼,m1方法的第一個參數類型是Car類型,Car類型上有注解Ann8
@Ann8
class Car {
}
public void m1(Car car) {
System.out.println("我是m1");
}
案例2
@Pointcut("@args(*,Ann8)"):匹配方法只有2個參數,且第2個參數所屬的類型上有Ann8注解
可以匹配下面代碼
@Ann8
class Car {
}
public void m1(String name,Car car) {
System.out.println("我是m1");
}
案例3
@Pointcut("@args(..,com.javacode2018.aop.demo9.test8.Ann8)"):匹配參數數量大於等於1,且最后一個參數所屬的類型上有Ann8注解
@Pointcut("@args(*,com.javacode2018.aop.demo9.test8.Ann8,..)"):匹配參數數量大於等於2,且第2個參數所屬的類型上有Ann8注解
@Pointcut("@args(..,com.javacode2018.aop.demo9.test8.Ann8,*)"):匹配參數數量大於等於2,且倒數第2個參數所屬的類型上有Ann8注解
這個案例代碼,大家自己寫一下,體驗一下。
@annotation
用法
@annotation(注解類型)
:匹配被調用的方法上有指定的注解。
案例
定義一個注解,可以用在方法上
package com.javacode2018.aop.demo9.test12;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Ann12 {
}
定義2個類
S12Parent為父類,內部定義了2個方法,2個方法上都有@Ann12注解
S12是代理的目標類,也是S12Parent的子類,內部重寫了m2方法,重寫之后m2方法上並沒有@Ann12注解,S12內部還定義2個方法m3和m4,而m3上面有注解@Ann12
package com.javacode2018.aop.demo9.test12;
class S12Parent {
@Ann12
public void m1() {
System.out.println("我是S12Parent.m1()方法");
}
@Ann12
public void m2() {
System.out.println("我是S12Parent.m2()方法");
}
}
public class S12 extends S12Parent {
@Override
public void m2() {
System.out.println("我是S12.m2()方法");
}
@Ann12
public void m3() {
System.out.println("我是S12.m3()方法");
}
public void m4() {
System.out.println("我是S12.m4()方法");
}
}
來個Aspect類
當被調用的目標方法上有@Ann12
注解的時,會被beforeAdvice處理。
package com.javacode2018.aop.demo9.test12;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AspectTest12 {
@Pointcut("@annotation(com.javacode2018.aop.demo9.test12.Ann12)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
測試用例
S12作為目標對象,創建代理,然后分別調用4個方法
@Test
public void test12() {
S12 target = new S12();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAspect(AspectTest12.class);
S12 proxy = proxyFactory.getProxy();
proxy.m1();
proxy.m2();
proxy.m3();
proxy.m4();
}
運行輸出
execution(void com.javacode2018.aop.demo9.test12.S12Parent.m1())
我是S12Parent.m1()方法
我是S12.m2()方法
execution(void com.javacode2018.aop.demo9.test12.S12.m3())
我是S12.m3()方法
我是S12.m4()方法
分析結果
m1方法位於S12Parent中,上面有@Ann12注解,被連接了,m3方法上有@Ann12
注解,被攔截了,而m4上沒有@Ann12
注解,沒有被攔截,這3個方法的執行結果都很容易理解。
重點在於m2方法的執行結果,沒有被攔截,m2方法雖然在S12Parent中定義的時候也有@Ann12
注解標注,但是這個方法被S1給重寫了,在S1中定義的時候並沒有@Ann12
注解,代碼中實際上調用的是S1中的m2方法,發現這個方法上並沒有@Ann12
注解,所以沒有被攔截。
bean
用法
bean(bean名稱)
:這個用在spring環境中,匹配容器中指定名稱的bean。
案例
來個類BeanService
package com.javacode2018.aop.demo9.test13;
public class BeanService {
private String beanName;
public BeanService(String beanName) {
this.beanName = beanName;
}
public void m1() {
System.out.println(this.beanName);
}
}
來個Aspect類
package com.javacode2018.aop.demo9.test13;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
public class Aspect13 {
//攔截spring容器中名稱為beanService2的bean
@Pointcut("bean(beanService2)")
public void pc() {
}
@Before("pc()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
來個spring配置類
package com.javacode2018.aop.demo9.test13;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy // 這個可以啟用通過AspectJ方式自動為符合條件的bean創建代理
public class MainConfig13 {
//將Aspect13注冊到spring容器
@Bean
public Aspect13 aspect13() {
return new Aspect13();
}
@Bean
public BeanService beanService1() {
return new BeanService("beanService1");
}
@Bean
public BeanService beanService2() {
return new BeanService("beanService2");
}
}
這個配置類中有個@EnableAspectJAutoProxy
,這個注解大家可能比較陌生,這個屬於aop中自動代理的范圍,詳情參考https://www.cnblogs.com/satire/p/14874849.html。
測試用例
下面啟動spring容器,加載配置類MainConfig13,然后分別獲取beanService1和beanService2,調用他們的m1方法,看看效果
@Test
public void test13() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig13.class);
//從容器中獲取beanService1
BeanService beanService1 = context.getBean("beanService1", BeanService.class);
beanService1.m1();
//從容器中獲取beanService2
BeanService beanService2 = context.getBean("beanService2", BeanService.class);
beanService2.m1();
}
運行輸出
beanService1
execution(void com.javacode2018.aop.demo9.test13.BeanService.m1())
beanService2
beanService2的m1方法被攔截了。
reference pointcut
表示引用其他命名切入點。
有時,我們可以將切入專門放在一個類中集中定義。
其他地方可以通過引用的方式引入其他類中定義的切入點。
語法如下:
@Pointcut("完整包名類名.方法名稱()")
若引用同一個類中定義切入點,包名和類名可以省略,直接通過方法就可以引用。
比如下面,我們可以將所有切入點定義在一個類中
package com.javacode2018.aop.demo9.test14;
import org.aspectj.lang.annotation.Pointcut;
public class AspectPcDefine {
@Pointcut("bean(bean1)")
public void pc1() {
}
@Pointcut("bean(bean2)")
public void pc2() {
}
}
下面頂一個一個Aspect類,來引用上面的切入點
package com.javacode2018.aop.demo9.test14;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Aspect14 {
@Pointcut("com.javacode2018.aop.demo9.test14.AspectPcDefine.pc1()")
public void pointcut1() {
}
@Pointcut("com.javacode2018.aop.demo9.test14.AspectPcDefine.pc1() || com.javacode2018.aop.demo9.test14.AspectPcDefine.pc2()")
public void pointcut2() {
}
}
組合型的pointcut
Pointcut
定義時,還可以使用&&、||、!運算符。
-
&&:多個匹配都需要滿足
-
||:多個匹配中只需滿足一個
-
!:匹配不滿足的情況下
@Pointcut("bean(bean1) || bean(bean2)") //匹配bean1或者bean2
@Pointcut("@target(Ann1) && @Annotation(Ann2)") //匹配目標類上有Ann1注解並且目標方法上有Ann2注解
@Pointcut("@target(Ann1) && !@target(Ann2)") // 匹配目標類上有Ann1注解但是沒有Ann2注解