文章目錄
一、AOP術語
1、通知(advice)
切面的工作被稱爲通知。通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還定義了何時執行這個工作。
Spring切面可以應用五種類型的通知:
- 前置通知(before):在目標方法被調用之前調用的通知;
- 後置通知(after):在目標方法成功執行之後調用,此時不會關心方法的返回是什麼;
- 返回通知(after-returning):在目標方法成功執行之後調用;
- 異常通知(after-throwing):在目標方法拋出異常後調用通知;
- 環繞通知(around):通知包裹了目標方法,在目標方法調用之前和之後執行自定義行爲,可以決定是否執行目標方法。
2、連接點(join point)
就是指spring允許你使用通知的地方
3、切點(point cut)
切點定義何時使用通知。切點的定義會匹配通知所要織入的一個或多個連接點。可以動態的決定是否使用通知
4、切面(aspect)
切面是切點和通知的結合。
5、引入(introduction)
引入允許我們向目標類添加新的方法和屬性,在不修改現有類的情況下,讓他們具有新的行爲和狀態
6、織入(weaving)
織入是把切面應用到目標對象並創建新的代理對象的過程。切面在指定的連接點被織入到目標對象中。在目標對象的生命週期裏有多少個點可以織入:
- 編譯期:切面在目標類的編譯時被織入。這種方式需要特殊的編譯器。AspectJ的織入編譯器就是以這種方式織入切面的
- 類加載期:切面在目標類加載到JVM時被織入。這種方式需要特殊的類加載器(ClassLoader),它可以在目標類被引入應用之前增強該類的字節碼。AspectJ5的加載時織入(load-time weaving,LTW)就是支持這種方式
- 運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP會爲目標類對象動態的創建一個代理對象。Spring AOP就是以這種方式。
二、指示器
Spring藉助AspectJ的切點表達式語言來定義Spring切面
- args():限制連接點匹配參數爲指定類型的執行方法
- @args():限制連接點由指定註解標註的執行方法,支持只有一個入參,且參數類持有指定註解
- execution():用於匹配是連接點的執行方法
- this():限制連接點匹配AOP代理的bean引用爲指定類型的類
- target():限制連接點匹配對象爲指定類型的類
- @target():限制連接點匹配特定的目標對象,要有指定的註解
- within():限制連接點匹配指定的類型
- @within():限制連接點匹配指定註解所標註的類型
- @annotation():限制匹配帶有指定註解的連接點
三、編碼使用
1、execution+args
定義一個重載的三個方法,用於驗證args
@Service("aopDemo1Service")
public class AopDemo1ServiceImpl implements IAopDemoService {
@Override
public String getDemo() {
System.out.println("demo1 start..............");
System.out.println("我是沒有入參的demo");
System.out.println("demo1 end..............");
return "no param!";
}
@Override
public String getDemo(String arg1) {
System.out.println("demo1 start..............");
System.out.println("我是有一個入參的demo,入參是:" + arg1);
System.out.println("demo1 end..............");
return "one param!";
}
@Override
public String getDemo(int arg1, String arg2) {
System.out.println("demo1 start..............");
System.out.println("我是有兩個入參的demo,第一個參數:" + arg1 + "\n第二個參數:" + arg2);
System.out.println("demo1 end..............");
return "two params!";
}
}
切面配置
@Aspect
@Component
public class AopConfigDemo {
@Pointcut("execution(* com.wfs.springboot.aop.*.getDemo(..))")
public void pointCut() {
}
/**
* args 用於匹配到第一個參數爲Integer、第二個參數爲String的連接點
* @param joinPoint
* @param arg1
* @param arg2
*/
@Around(value = "pointCut() && args(arg1,arg2)")
public void around(ProceedingJoinPoint joinPoint, Integer arg1,String arg2) {
System.out.println("aop start................");
try {
Object proceed = joinPoint.proceed();
System.out.println("目標方法返回值:" + proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("aop end................");
}
}
2、使用自定義註解@annotation
在類上和方法上加上註解
@Service("aopDemo2Service")
@AopAnnotation
public class AopDemo2ServiceImpl implements IAopDemoService{
@Override
@AopAnnotation
public String getDemo() {
System.out.println("demo2 start..............");
System.out.println("我是沒有入參的demo");
System.out.println("demo2 end..............");
return "no param!";
}
@Override
public String getDemo(String arg) {
System.out.println("demo2 start..............");
System.out.println("我是有一個入參的demo,入參是:" + arg);
System.out.println("demo2 end..............");
return "one param!";
}
@Override
public String getDemo(int arg1, String arg2) {
System.out.println("demo2 start..............");
System.out.println("我是有兩個入參的demo,第一個參數:" + arg1 + "\n第二個參數:" + arg2);
System.out.println("demo2 end..............");
return "two params!";
}
aop配置
/**
* 只有方法上有註解纔會使用切面
*/
@Pointcut("@annotation(com.wfs.springboot.annotation.AopAnnotation)")
public void pointCut() {
}
/**
* args 用於匹配到第一個參數爲Integer、第二個參數爲String的連接點
* @param joinPoint
*/
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("aop start................");
try {
Object proceed = joinPoint.proceed();
System.out.println("目標方法返回值:" + proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("aop end................");
}
3、@winthin、winthin
/**
* 使用@within 當前註解的類所有方法都會被織入
*/
@Pointcut("@within(com.wfs.springboot.annotation.AopAnnotation)")
public void pointCut() {
}
/**
* IAopDemoService類型及子類型的任何方法
*/
@Pointcut("within(com.wfs.springboot.aop.IAopDemoService+)")
public void pointCut() {
}
within支持表達式,類似execution
4、@args
僅支持只有一個入參的方法,並且參數類上必須持有定義的註解
aop配置
@Around(value = "pointCut() && @args(com.wfs.springboot.annotation.AopAnnotation)")
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("aop start................");
try {
Object proceed = joinPoint.proceed();
System.out.println("目標方法返回值:" + proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("aop end................");
}
入參類
@Data
@AopAnnotation
public class User {
private String name;
private SexEnum sex;
}
調用方法
@Override
public void getDemo(User user) {
System.out.println("我有帶註解的入參");
}
5、this
只支持全限定名,不支持通配符;是對代理對象類型的匹配所以包括接口類型也匹配
當前目標對象實現了IAopDemoService接口的任何方法
@Pointcut("this(com.wfs.springboot.aop.IAopDemoService)")
public void pointCut() {
}
6、target、@target
只支持全限定名,不支持通配符;是對目標對象類型的匹配所以不包括接口類型匹配
@Pointcut("target(com.wfs.springboot.aop.AopDemo1ServiceImpl)")
public void pointCut() {
}
@Pointcut("within(com.wfs.springboot.aop.IAopDemoService+) && @target(com.wfs.springboot.annotation.AopAnnotation)")
public void pointCut() {
}
@target不能單獨使用,啓動會報錯
7、bean
支持通配符
/**
* 表示bean的id或者名稱爲aopDemo1Service
*/
@Pointcut("bean(aopDemo1Service)")
public void pointCut() {
}
四、通知參數
public interface JoinPoint {
// 連接點的信息execution(String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(String))
String toString();
//連接點簡要信息 execution(AopDemo1ServiceImpl.getDemo(..))
String toShortString();
//連接點詳細信息execution(public java.lang.String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(java.lang.String))
String toLongString();
//返回代理對象
Object getThis();
//返回目標對象
Object getTarget();
// 返回目標參數
Object[] getArgs();
// 返回當前連接點的簽名
Signature getSignature();
// 返回連接點在類文件中的位置
SourceLocation getSourceLocation();
//連接點類型 method-execution
String getKind();
// 返回連接點靜態部分
JoinPoint.StaticPart getStaticPart();
public interface Signature {
//String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(int,String)
String toString();
//AopDemo1ServiceImpl.getDemo(..)
String toShortString();
//public java.lang.String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(int,java.lang.String)
String toLongString();
// 方法名 getDemo
String getName();
// 表示修飾符 1
int getModifiers();
// 目標類
Class getDeclaringType();
// 目標類全限定名
String getDeclaringTypeName();
}