本文內(nèi)容
- Spring 10種切點(diǎn)表達(dá)式詳解
- 切點(diǎn)的組合使用
- 公共切點(diǎn)的定義
聲明切點(diǎn)@Poincut
@Poincut 的使用格式如下:
@Poincut("PCD") // 切點(diǎn)表達(dá)式 表示對(duì)哪些方法進(jìn)行增強(qiáng)
public void pc(){} // 切點(diǎn)簽名,返回值必須為void
10種切點(diǎn)表達(dá)式
AspectJ的切點(diǎn)指示符AspectJ pointcut designators (PCD) ,也就是俗稱的切點(diǎn)表達(dá)式,Spring中支持10種,如下表:
表達(dá)式類型 |
作用 |
匹配規(guī)則 |
execution |
用于匹配方法執(zhí)行的連接點(diǎn) |
|
within |
用于匹配指定類型內(nèi)的方法執(zhí)行 |
within(x)匹配規(guī)則target.getClass().equals(x) |
this |
用于匹配當(dāng)前AOP代理對(duì)象類型的執(zhí)行方法,包含引入的接口類型匹配 |
this(x)匹配規(guī)則: |
target |
用于匹配當(dāng)前目標(biāo)對(duì)象類型的執(zhí)行方法,不包括引入接口的類型匹配 |
target(x)匹配規(guī)則:x.getClass().isAssignableFrom(target.getClass()); |
args |
用于匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)為指定類型的執(zhí)行方法 |
傳入的目標(biāo)位置參數(shù).getClass().equals(@args(對(duì)應(yīng)的參數(shù)位置的注解類型))!= null |
@target |
用于匹配當(dāng)前目標(biāo)對(duì)象類型的執(zhí)行方法,其中目標(biāo)對(duì)象持有指定的注解 |
|
@args |
用于匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)持有指定注解的執(zhí)行 |
傳入的目標(biāo)位置參數(shù).getClass().getAnnotation(@args(對(duì)應(yīng)的參數(shù)位置的注解類型))!= null |
@within |
用于匹配所有持有指定注解類型內(nèi)的方法 |
被調(diào)用的目標(biāo)方法Method對(duì)象.getDeclaringClass().getAnnotation(within中指定的注解類型) != null |
@annotation |
用于匹配當(dāng)前執(zhí)行方法持有指定注解的方法 |
target.getClass().getMethod("目標(biāo)方法名").getDeclaredAnnotation(@annotation(目標(biāo)注解))!=null |
bean |
Spring AOP擴(kuò)展的,AspectJ沒有對(duì)應(yīng)的指示符,用于匹配特定名稱的Bean對(duì)象的執(zhí)行方法 |
|
簡單介紹下AspectJ中常用的3個(gè)通配符:
- *:匹配任何數(shù)量的字符
- ..:匹配任何數(shù)量字符的重復(fù),如任何數(shù)量子包,任何數(shù)量方法參數(shù)
- +:匹配指定類型及其子類型,僅作為后綴防過載類型模式后面。
execution
用于匹配方法執(zhí)行,最常用。
格式說明
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
- 其中帶 ?號(hào)的 modifiers-pattern?,declaring-type-pattern?,throws-pattern?是可選項(xiàng)
- ret-type-pattern,name-pattern, parameters-pattern是必選項(xiàng)
- modifier-pattern? 修飾符匹配,如public 表示匹配公有方法,*表示任意修飾符
- ret-type-pattern 返回值匹配,* 表示任何返回值,全路徑的類名等
- declaring-type-pattern? 類路徑匹配
- name-pattern 方法名匹配,* 代表所有,xx*代表以xx開頭的所有方法
- (param-pattern) 參數(shù)匹配,指定方法參數(shù)(聲明的類型),(..)代表所有參數(shù),(*,String)代表第一個(gè)參數(shù)為任何值,第二個(gè)為String類型,(..,String)代表最后一個(gè)參數(shù)是String類型
- throws-pattern? 異常類型匹配
舉例說明
public class PointcutExecution {
// com.crab.spring.aop.demo02包下任何類的任意方法
@Pointcut("execution(* com.crab.spring.aop.demo02.*.*(..))")
public void m1(){}
// com.crab.spring.aop.demo02包及其子包下任何類的任意方法
@Pointcut("execution(* com.crab.spring.aop.demo02..*.*(..))")
public void m2(){}
// com.crab.spring.aop包及其子包下IService接口的任意無參方法
@Pointcut("execution(* com.crab.spring.aop..IService.*(..))")
public void m3(){}
// com.crab.spring.aop包及其子包下IService接口及其子類型的任意無參方法
@Pointcut("execution(* com.crab.spring.aop..IService+.*(..))")
public void m4(){}
// com.crab.spring.aop.demo02.UserService類中有且只有一個(gè)String參數(shù)的方法
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(String))")
public void m5(){}
// com.crab.spring.aop.demo02.UserService類中參數(shù)個(gè)數(shù)為2且最后一個(gè)參數(shù)類型是String的方法
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(*,String))")
public void m6(){}
// com.crab.spring.aop.demo02.UserService類中最后一個(gè)參數(shù)類型是String的方法
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..,String))")
public void m7(){}
}
within
格式說明
within(類型表達(dá)式):目標(biāo)對(duì)象target的類型是否和within中指定的類型匹配
匹配規(guī)則: target.getClass().equals(within表達(dá)式中指定的類型)
舉例說明
public class PointcutWithin {
// 匹配 com.crab.spring.aop.demo02包及其子包下任何類的任何方法
@Pointcut("within(com.crab.spring.aop.demo02..*)")
public void m() {
}
// 匹配m.crab.spring.aop.demo02包及其子包下IService類型及其子類型的任何方法
@Pointcut("within(com.crab.spring.aop.demo02..IService+)")
public void m2() {
}
// 匹配com.crab.spring.aop.demo02.UserService類中所有方法,不含其子類
@Pointcut("within(com.crab.spring.aop.demo02.UserService)")
public void m3() {
}
}
this
格式說明
this(類型全限定名):通過aop創(chuàng)建的代理對(duì)象的類型是否和this中指定的類型匹配;this中使用的表達(dá)式必須是類型全限定名,不支持通配符。
this(x)的匹配規(guī)則是:x.getClass.isAssingableFrom(proxy.getClass)
舉例說明
package com.crab.spring.aop.demo02.aspectj;
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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
*/
@Aspect
public class PointcutThis {
interface I1{
void m();
}
static class C1 implements I1{
@Override
public void m() {
System.out.println("C1 m()");
}
}
// 匹配 I1類型或是其子類
@Pointcut("this(com.crab.spring.aop.demo02.aspectj.PointcutThis.I1)")
public void pc(){}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
// proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutThis.class);
// 獲取代理
I1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判斷代理對(duì)象是否是C1類型的
System.out.println(C1.class.isAssignableFrom(proxy.getClass()));
}
}
來觀察下輸出
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutThis$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
使用JDK動(dòng)態(tài)代理生成的代理對(duì)象,其類型是I1類型。
思考下:將切點(diǎn)表達(dá)式改成下面的輸出結(jié)果是?
// 匹配 C1類型或是其子類
@Pointcut("this(
com.crab.spring.aop.demo02.aspectj.PointcutThis.C1)")
public void pc(){}
target
格式說明
target(類型全限定名):判斷目標(biāo)對(duì)象的類型是否和指定的類型匹配;表達(dá)式必須是類型全限定名,不支持通配符。
target(x)匹配規(guī)則:x.getClass().isAssignableFrom(target.getClass());
舉例說明
@Aspect
public class PointcutTarget {
interface I1{
void m();
}
static class C1 implements I1{
@Override
public void m() {
System.out.println("C1 m()");
}
}
// 匹配目標(biāo)類型必須是
@Pointcut("target(com.crab.spring.aop.demo02.aspectj.PointcutTarget.C1)")
public void pc(){}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutTarget.class);
// 獲取代理
I1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判斷代理對(duì)象是否是C1類型的
System.out.println(C1.class.isAssignableFrom(target.getClass()));
}
}
輸出結(jié)果
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutTarget$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
args
格式說明
args(參數(shù)類型列表)匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)是否為args中指定的類型;參數(shù)類型列表中的參數(shù)必須是類型全限定名,不支持通配符;args屬于動(dòng)態(tài)切入點(diǎn),也就是執(zhí)行方法的時(shí)候進(jìn)行判斷的,開銷非常大,非特殊情況最好不要使用。
args(String) // 方法個(gè)數(shù)為1,類型是String
args(*,String) // 方法參數(shù)個(gè)數(shù)2,第2個(gè)是String類型
args(..,String) // 方法個(gè)數(shù)不限制,最后一個(gè)必須是String
舉例說明
package com.crab.spring.aop.demo02.aspectj;
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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
*/
@Aspect
public class PointcutArgs {
interface I1{
void m(Object name);
}
static class C1 implements I1{
@Override
public void m(Object name) {
String type = name.getClass().getName();
System.out.println("C1 m() 參數(shù)類型 " + type);
}
}
// 匹配方法參數(shù)個(gè)數(shù)1且類型是必須是String
@Pointcut("args(String)")
public void pc(){}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutArgs.class);
// 獲取代理
I1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
proxy.m("xxxx");
proxy.m(100L);
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判斷代理對(duì)象是否是C1類型的
System.out.println(C1.class.isAssignableFrom(target.getClass()));
}
}
觀察下輸出
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutArgs$C1.m(Object))
C1 m() 參數(shù)類型 JAVA.lang.String
C1 m() 參數(shù)類型 java.lang.Long
JDK代理? false
CGLIB代理? true
true
參數(shù)類型傳遞是String時(shí)候增強(qiáng)了,而Long的時(shí)候沒有執(zhí)行增強(qiáng)方法。
@within
格式說明
@within(注解類型):匹配指定的注解內(nèi)定義的方法。
匹配規(guī)則: 被調(diào)用的目標(biāo)方法Method對(duì)象.getDeclaringClass().getAnnotation(within中指定的注解類型) != null
舉例說明
package com.crab.spring.aop.demo02.aspectj;
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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @關(guān)于我 請(qǐng)關(guān)注公眾號(hào) 螃蟹的Java筆記 獲取更多技術(shù)系列
*/
@Aspect
public class PointcutAnnWithin {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnn {
}
interface I1 {
void m();
}
@MyAnn
static class C1 implements I1 {
@Override
public void m() {
System.out.println("C1 m()");
}
}
// 匹配目標(biāo)類型必須上必須有注解MyAnn
@Pointcut("@within(com.crab.spring.aop.demo02.aspectj.PointcutAnnWithin.MyAnn)")
public void pc() {
}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnWithin.class);
// 獲取代理
I1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判斷代理對(duì)象是否是C1類型的
System.out.println(C1.class.isAssignableFrom(target.getClass()));
}
}
輸出
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnWithin$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
思考下父類上有注解,子類繼承父類的方法,同時(shí)考慮下注解@Inherited是否在切點(diǎn)注解的場(chǎng)景?
@target
格式說明
@target(注解類型):判斷目標(biāo)對(duì)象target類型上是否有指定的注解;@target中注解類型也必須是全限定類型名。
匹配規(guī)則: target.class.getAnnotation(指定的注解類型) != null
注意,如果目標(biāo)注解是標(biāo)注在父類上的,那么定義目標(biāo)注解時(shí)候應(yīng)使用@Inherited標(biāo)注,使子類能繼承父類的注解。
舉例說明
package com.crab.spring.aop.demo02.aspectj;
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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;
import java.lang.annotation.*;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
*/
@Aspect
public class PointcutAnnTarget {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited // 子類能繼承父類的注解
@interface MyAnn2 {
}
@MyAnn2 // 注解在父類上
static class P1 {
void m(){}
}
static class C1 extends P1 {
@Override
public void m() {
System.out.println("C1 m()");
}
}
// 匹配目標(biāo)類型必須上必須有注解MyAnn
@Pointcut("@target(com.crab.spring.aop.demo02.aspectj.PointcutAnnTarget.MyAnn2)")
public void pc() {
}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnTarget.class);
// 獲取代理
C1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
// 目標(biāo)類上是否有切點(diǎn)注解
System.out.println(target.getClass().getAnnotation(MyAnn2.class)!= null);
}
}
輸出結(jié)果
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnTarget$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true
從結(jié)果最后一行看,目標(biāo)對(duì)象繼承了父類的注解,符合@target的切點(diǎn)規(guī)則。
@args
格式說明
@args(注解類型):方法參數(shù)所屬的類上有指定的注解;注意不是參數(shù)上有指定的注解,而是參數(shù)類型的類上有指定的注解。和args類似,不過針對(duì)的是參數(shù)類型上的注解。
匹配規(guī)則: 傳入的目標(biāo)位置參數(shù).getClass().getAnnotation(@args(對(duì)應(yīng)的參數(shù)位置的注解類型))!= null
舉例說明
package com.crab.spring.aop.demo02.aspectj;
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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;
import java.lang.annotation.*;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
*/
@Aspect
public class PointcutAnnArgs {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited // 子類能繼承父類的注解
@interface MyAnn3 {
}
@MyAnn3
static class MyParameter{
}
static class C1 {
public void m(MyParameter myParameter) {
System.out.println(myParameter.getClass().getAnnotation(MyAnn3.class));
System.out.println("C1 m()");
}
}
// 匹配方法上最后的一個(gè)參數(shù)類型上有注解MyAnn3
@Pointcut("@args(..,com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs.MyAnn3)")
public void pc() {
}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnArgs.class);
// 獲取代理
C1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
MyParameter myParameter = new MyParameter();
proxy.m(myParameter);
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
// 目標(biāo)類上是否有切點(diǎn)注解
System.out.println(myParameter.getClass().getAnnotation(MyAnn3.class)!= null);
}
}
觀察結(jié)果
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs$C1.m(MyParameter))
@com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs$MyAnn3()
C1 m()
JDK代理? false
CGLIB代理? true
true
第二行中目標(biāo)方法上輸出了參數(shù)的注解。
最后一行判斷參數(shù)類型上確實(shí)有注解。
@annotation
格式說明
@annotation(注解類型):匹配被調(diào)用的目標(biāo)對(duì)象的方法上有指定的注解
匹配規(guī)則:target.getClass().getMethod("目標(biāo)方法名").getDeclaredAnnotation(@annotation(目標(biāo)注解))!=null
這個(gè)在針對(duì)特定注解的方法日志攔截場(chǎng)景下應(yīng)用比較多。
舉例說明
package com.crab.spring.aop.demo02.aspectj;
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.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;
import java.lang.annotation.*;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
*/
@Aspect
public class PointcutAnnotation {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnn4 {
}
/**
* 父類 方法上都有@MyAnn4
*/
static class P1{
@MyAnn4
public void m1(){
System.out.println("P1 m()");
}
@MyAnn4
public void m2(){
System.out.println("P1 m2()");
}
}
/**
* 子類
* 注意重新重寫了父類的m1方法但是沒有聲明注解@Ann4
* 新增了m3方法帶注解@Ann4
*/
static class C1 extends P1 {
@Override
public void m1() {
System.out.println("C1 m1()");
}
@MyAnn4
public void m3() {
System.out.println("C1 m3()");
}
}
// 匹配調(diào)用的方法上必須有注解
@Pointcut("@annotation(com.crab.spring.aop.demo02.aspectj.PointcutAnnotation.MyAnn4)")
public void pc() {
}
@Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
}
public static void main(String[] args) throws NoSuchMethodException {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 獲取C1上所有接口 spring工具類提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 設(shè)置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnotation.class);
// 獲取代理
C1 proxy = proxyFactory.getProxy();
// 調(diào)用方法
proxy.m1();
proxy.m2();
proxy.m3();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
// 目標(biāo)對(duì)象的目標(biāo)方法上是否直接聲明了注解MyAnn4
System.out.println(target.getClass().getMethod("m1").getDeclaredAnnotation(MyAnn4.class)!=null);
System.out.println(target.getClass().getMethod("m2").getDeclaredAnnotation(MyAnn4.class)!=null);
System.out.println(target.getClass().getMethod("m3").getDeclaredAnnotation(MyAnn4.class)!=null);
}
}
觀察下結(jié)果
C1 m1()
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnotation$P1.m2())
P1 m2()
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnotation$C1.m3())
C1 m3()
JDK代理? false
CGLIB代理? true
false
true
true
簡單分析下:
- C1中重寫了m1方法,上面有沒有 @Ann4,所有方法沒有被攔截
- 其它的m2在父類上有注解@Ann4,m3在子類上也有注解@Ann4,所以攔截了。
- 最后3行輸出了目標(biāo)對(duì)象的3個(gè)方法上是否有注解的情況。
bean
格式說明
bean(bean名稱):這個(gè)用在spring環(huán)境中,匹配容器中指定名稱的bean。
匹配格式:ApplicationContext.getBean("bean表達(dá)式中指定的bean名稱") != null
舉例說明
定義一個(gè)bean
package com.crab.spring.aop.demo02.aspectj;
/**
* @author zfd
* @version v1.0
* @date 2022/2/6 23:30
*/
public class MyBean {
private String beanName;
public MyBean(String beanName) {
this.beanName = beanName;
}
public void m() {
System.out.println("我是" + this.beanName);
}
}
切面中的切點(diǎn)和通知定義
@Aspect
public class PointcutBean {
// 容器中bean名稱是"myBean1"的方法進(jìn)行攔截
@Pointcut("bean(myBean1)")
public void pc() {
}
@Before("pc()")
public void m(JoinPoint joinPoint) {
System.out.println("start " + joinPoint);
}
}
組合使用
@Aspect
@Configuration
@EnableAspectJAutoProxy // 自動(dòng)生成代理對(duì)象
public class PointcutBeanConfig {
// 注入 myBean1
@Bean("myBean1")
public MyBean myBean1() {
return new MyBean("myBean1");
}
// myBean2
@Bean("myBean2")
public MyBean myBean2() {
return new MyBean("myBean2");
}
// 注入切面
@Bean("pointcutBean")
public PointcutBean pointcutBean() {
return new PointcutBean();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(PointcutBeanConfig.class);
MyBean myBean1 = context.getBean("myBean1", MyBean.class);
myBean1.m();
MyBean myBean2 = context.getBean("myBean2", MyBean.class);
myBean2.m();
}
}
觀察下結(jié)果
start execution(void com.crab.spring.aop.demo02.aspectj.MyBean.m())
我是myBean1
我是myBean2
myBean1的方法被攔截了。
上面介紹了Spring中10中切點(diǎn)表達(dá)式,下面介紹下切點(diǎn)的組合使用和公共切點(diǎn)的抽取。
切點(diǎn)的組合
切點(diǎn)與切點(diǎn)直接支持邏輯邏輯組合操作: && 、||、 !。使用較小的命名組件構(gòu)建更復(fù)雜的切入點(diǎn)表達(dá)式是最佳實(shí)踐。
同一個(gè)類內(nèi)切點(diǎn)組合
public class CombiningPointcut {
/**
* 匹配 com.crab.spring.aop.demo02包及子包下任何類的public方法
*/
@Pointcut("execution(public * com.crab.spring.aop.demo02..*.*(..))")
public void publicMethodPc() {
}
/**
* com.crab.spring.aop.demo02.UserService類的所有方法
*/
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..))")
public void serviceMethodPc(){}
/**
* 組合的切點(diǎn)
*/
@Pointcut("publicMethodPc() && serviceMethodPc()")
public void combiningPc(){
}
/**
* 組合的切點(diǎn)2
*/
@Pointcut("publicMethodPc() || !serviceMethodPc()")
public void combiningPc2(){
}
}
不同類之間切點(diǎn)組合
切點(diǎn)方法的可見性會(huì)影響組合但是不影響切點(diǎn)的匹配。
public class CombiningPointcut2 {
/**
* com.crab.spring.aop.demo02.UserService類的所有方法
*/
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..))")
public void serviceMethodPc2(){}
/**
* 組合的切點(diǎn),跨類組合
*/
@Pointcut("com.crab.spring.aop.demo02.aspectj.reuse.CombiningPointcut.publicMethodPc() && serviceMethodPc2()")
public void combiningPc(){
}
/**
* 組合的切點(diǎn),跨類組合,由于serviceMethodPc是private, 此處無法組合
*/
@Pointcut("com.crab.spring.aop.demo02.aspectj.reuse.CombiningPointcut.serviceMethodPc() && serviceMethodPc2()")
public void combiningPc2(){
}
}
切點(diǎn)的公用
在使用企業(yè)應(yīng)用程序時(shí),開發(fā)人員通常希望從多個(gè)方面引用應(yīng)用程序的模塊和特定的操作集。建議為此目的定義一個(gè)捕獲公共切入點(diǎn)表達(dá)式的 CommonPointcuts 方面。直接看案例。
不同層的公共切點(diǎn)
/**
* 公用的切點(diǎn)
* @author zfd
* @version v1.0
* @date 2022/2/7 8:53
*/
public class CommonPointcuts {
/**
* web層的通用切點(diǎn)
*/
@Pointcut("within(com.xyz.myapp.web..*)")
public void inWebLayer() {}
@Pointcut("within(com.xyz.myapp.service..*)")
public void inServiceLayer() {}
@Pointcut("within(com.xyz.myapp.dao..*)")
public void inDataAccessLayer() {}
@Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
public void businessService() {}
@Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
public void dataAccessOperation() {}
}
程序中可以直接引用這些公共的切點(diǎn)
/**
* 使用公共的切點(diǎn)
* @author zfd
* @version v1.0
* @date 2022/2/7 8:56
*/
@Aspect
public class UseCommonPointcuts {
/**
* 直接使用公共切點(diǎn)
*/
@Before("com.crab.spring.aop.demo02.aspectj.reuse.CommonPointcuts.inWebLayer()")
public void before(JoinPoint joinPoint){
System.out.println("before:" + joinPoint);
}
}
總結(jié)
本文介紹Spring中10種切點(diǎn)表達(dá)式,最常用的是execution,同時(shí)介紹切點(diǎn)如何組合使用和如何抽取公共的切點(diǎn)。
原文
https://www.cnblogs.com/kongbubihai/p/16017046.html