其實人最大悲哀莫過於知道本身想要什麼,殊不知道怎麼堅持!最近迷戀上了死侍 其實和我平時的狀態差很少,以一個混子的心態去作任何事情,每每成功的機率會更大!!!java
一張圖片鎮樓!!!android
上文說到了AspectJ的集成問題,若是沒有看過上一篇文章的小夥伴能夠看看本系列的第一篇文章。git
AOP埋點從入門到放棄(一)github
這篇文章充分的講解了關於AspectJ的集成問題,接下來咱們講講怎麼更好的使用AspectJ來惟我所用。。。編程
其實我感受這個東西提及來是最難的,由於要記住一大堆概念!其實記憶力是個人最煩的東西,可是我是一隻猿,一隻牛逼的猿!因此當背課文了...bash
先來看一段代碼app
@Aspect
public class TraceAspect {
private static final String TAG = "hjl";
@Before("execution(* android.app.Activity.on*(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) {
String key = joinPoint.getSignature().toString();
Log.e(TAG, "onActivityMethodBefore: 切面的點執行了!" + key);
}
}
複製代碼
先說一下這段代碼實現了什麼?主要實現了在Activity中全部以on開頭的方法前面打印一段**Log.e(TAG, "onActivityMethodBefore: 切面的點執行了!" + key);**代碼!接下來咱們來逐一講解!框架
關於@Aspect這個註解,是下面全部內容的基礎,若是沒有這個註解AspectJ沒有相應的入口,就不會有相應的切面了!AspectJ會找到全部@Aspect註解,而後函數
首先說一下通配符的大致格式:(我也不知道理解的對不對,可是項目中使用的時候沒有發現什麼不對的地方)post
@註解 | 訪問權限 | 返回值類型 | 類名 | 函數名 | 參數 |
---|
大致上的通配符都是這個格式的,咱們用上面的一個通配符去說明一下:
execution(* android.app.Activity.on*(..))
複製代碼
execution 通常指定方法的執行,在日後由於沒有註解和訪問權限的限制,因此這裏什麼也沒寫,返回值用*代替,說明能夠用任何返回值,android.app.Activity.on*表明函數名稱的全路徑,後面的一個型號表明on後面能夠接任何東西,後面的(..)表明其參數能夠爲任何值。
上面就是一段通配符的含義了!其實學習AspectJ的時候,我以爲最難懂的就是相應的操做符了,若是操做符弄明白了的話,真的就很簡單了!可是若是以前作事後臺的話,這個應該就很簡單了,就是Spring框架中的AOP是同樣的都是用的Pointcut語法。由於本身不是java後臺開發人員,因此解釋的可能不到位,你能夠去找大家java後臺組的人去問問,學習一下!應該比我講的強不少,由於我真是第一次接觸這個東西!
由於平時沒接觸過,因此這裏就寫一些經常使用的吧!
分類
JPoint | 說明 | Pointcut語法說明 |
---|---|---|
method execution | 通常指定方法的執行 | execution(MethodSignnature) |
method call | 函數被調用 | call(MethodSignnature) |
constructor call | 構造函數被調用 | call(ConstructorSignature) |
constructor execution | 構造函數執行內部 | execution(ConstructorSignature) |
field get | 讀變量 | get(FieldSIgnature) |
field set | 寫變量 | set(FieldSIgnature) |
handler | 異常處理 | handler(TypeSignature) 注意:只能和@Before()配合使用,不支持@After、@Around等 |
advice execution | advice執行 | adciceexectuin() |
Signature參考
Sigbature | 語法(間隔一個空格) |
---|---|
MethodSignature | @註解 訪問權限 返回值類型 類名.函數名(參數) |
ConstructorSignature | @註解 訪問權限 類名.new(參數) |
FieldSignature | @註解 訪問權限 變量類型 類名.類成員變量名 |
Signature語法明細
Sigbature語法明細 | 解釋 |
---|---|
@註解 | @完整類名,若是沒有則不寫 |
訪問權限 | public/private/portect,以及static/final,若是沒有則不寫 注意:若是隻寫public則匹配出來的是所有,若是寫public final則匹配的是全部public final開頭的內容 |
返回值類型 | 若是不限定類型,使用通配符*表示 |
類名.函數名 | 可使用的通配符,包括*和..以及+號。其中*號用於陪陪除.號以外的任意字符,而..則表示任意字package,+號表示子類 注意:1.ConstructorSignature的函數名只能爲new 2.(.函數名能夠不寫),重用和註解一塊兒使用 3.不能以..開頭 |
變量類型 | 成員變量類型,*表明任意類型 |
類名.成員變量名 | 類名可使用通配符,與函數。函數名相似 |
Advice內容
Advice | 說明 |
---|---|
@Before(Pointcut) | 執行在jPoint以前 |
@After(Pointcut) | 執行在jPoint以後 |
@Around(Pointcut) | 替代原來的代碼,若是要執行原來的代碼,須要使用proceedingJoinPoint.proceed(); 注意:不能夠和@After和@Before等一塊兒使用 |
上面這寫表的你先簡單看一下,估計你一會仍是會回來看的!!!
@Pointcut("call(* com.jinlong.aspectjdemo.MainActivity.callMethod(..))")
public void callMethod() {
//爲了演示call方法的使用
}
@Before("callMethod()")
public void beforeCallMethod(JoinPoint joinPoint) {
Log.e(TAG, "call方法的演示");
}
複製代碼
說一下上面代碼:@Pointcut是來註解方法的,call後面添加了一系列的通配符,簡單說就是一個方法的地址,*表明沒有返回值,@Before是說明在切片前執行!這裏千萬別把com.jinlong.aspectjdemo.MainActivity.callMethod這個地址寫錯了就行!
其實上面這段代碼能夠簡化爲
@Before("call(* com.jinlong.aspectjdemo.MainActivity.callMethod(..))")
public void beforeCallMethod(JoinPoint joinPoint){
Log.e(TAG, "call方法的演示");
}
複製代碼
若是你把上面的@Before換爲@After,那麼就會在方法以後打印!!!
再來看一段代碼:
@Around("call(* com.jinlong.aspectjdemo.MainActivity.callMethod(..))")
public void aroundCallMethod(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long endTime = System.currentTimeMillis();
Log.e(TAG, "方法執行的時間爲" + (endTime - startTime));
}
複製代碼
這段代碼是統計方法執行時間的,這裏着重講兩個內容
因此這裏就能計算出方法的執行時間了!!!也就是@Around的用法了!
以前的時候,我總以爲execution和call是同樣的,可是後來我知道了,他們最大的區別是這樣的!!!
好比你有一個方法:callMethod()對吧! 而後使用call是這個樣子滴~
call的相應方法();
callMethod();
複製代碼
可是若是是execution的話就編程這個樣子滴了~
callMethod(){
execution的相應方法();
}
複製代碼
其餘的就沒有什麼區別了,也就不在這裏舉例說明了。
先說下這個東西是構造方法上用的,也就是說針對於相應的構造方法進行相應切面操做的!可是我一隻有一個疑問不明白,若是我按照上面方法的通配符進行操做的話,按照常理說應該也是能在相應切面進行操做的纔對啊!編譯不報錯,但就是怎麼也打印不出來結果,還請明白的大神幫我解答一下!
按照上面的表格還有一種方案解決相應構造方法的問題
@Before("execution(com.jinlong.aspectjdemo.Person.new(..))")
public void beforeConstructorExecution(JoinPoint joinPoint) {
//這個是顯示Constructor的
Log.e(TAG, "before->" + joinPoint.getThis().toString() + "#" + joinPoint.getSignature().getName());
}
複製代碼
這段代碼我嘗試過了,能夠打印出結果。也就是以**ConstructorSignature @註解 訪問權限 類名.new(參數)**這種方式就能夠打印出相應結果來!不過我勸你把那個@Before換成@After不然你打印出來的內容多是一個空!
就是至關你能夠修改類的成員變量,無論你怎麼設置最終返回的都是你設定的值!
看下面這段代碼:
這裏是正常的一個類:
public class Person {
private String name;
private String age;
public Person() {
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
複製代碼
這纔是核心代碼!
@Around("get(String com.jinlong.aspectjdemo.Person.age)")
public String aroundFieldGet(ProceedingJoinPoint joinPoint) throws Throwable {
// 執行原代碼
Object obj = joinPoint.proceed();
String age = obj.toString();
Log.e(TAG, "age: " + age);
return "100";
}
複製代碼
這裏用到的的是上面FieldSignature @註解 訪問權限 變量類型 類名.類成員變量名上面這段代碼的含義是這樣滴,在每次用到age這個內容的時候,都會被修改,可是有一個問題,就是你不能重寫類的**toString()**方法,若是你重寫了這個方法的話,obj返回的就是一個空!我還真不知道爲何,還請明白的告知一二!,這裏面get是一個關鍵字,就這麼理解吧!由於沒有設置訪問權限和註解,因此這裏直接就省返回的變量類型(String類型)和類成員變量名的全路徑了!
在看下面這段代碼:
@Around("set(String com.jinlong.aspectjdemo.Person.age)")
public void aroundFieleSet(ProceedingJoinPoint joinPoint) {
Log.e(TAG, "aroundFieleSet: " + joinPoint.getTarget().toString() +
joinPoint.getSignature().getName());
}
複製代碼
這個能夠對相應的age屬性進行設置的方法,也就是當發生賦值操做的時候都會被修改!這裏和你們說明一下,上面這段代碼沒有**joinPoint.proceed();**代碼,因此以前的代碼中執行的內容就會失效了!也就是說被打印這段話替換了!其實上面這段代碼你運行的時候你會發現一件事,Log被打印了兩次,爲何呢?你想啊!this.age = age;在set方法中出現一次,並且還在構造方法中出現一次呢。仔細看看,因此這裏要排除構造方法總的那一次,怎麼處理呢?就要用到 withincode了!
怎麼理解這個東西呢?表示某個構造方法或函數中涉及到的JPoint。不理解吧!不要緊,看一段代碼你就理解了!
@Around("set(String com.jinlong.aspectjdemo.Person.age)&&!withincode(com.jinlong.aspectjdemo.Person.new(..))")
public void aroundFieleSet(ProceedingJoinPoint joinPoint) throws Throwable {
//設置相應的成員變量
joinPoint.proceed();
}
複製代碼
在1.2.4上面說到set會在兩個地方都有,可是其實我是不想要構造方法中的那個的,怎麼把他排除呢?那就是後面添加的這句代碼**com.jinlong.aspectjdemo.Person.new(..))**就是把構造方法中的內容排除!其實很好理解,就是排除相應的構造方法,能夠簡單理解withincode就是帶着某個內容,可是因爲取反了,因此就是不帶着這個東西了!!!就醬紫了。。。
這個相比較之下就簡單一點了,直接上代碼:
這是在代碼中的一個異常,很簡單的一個異常,若是這個方法走了相應的catch,那麼就能捕獲相應的異常了!
private void catchMethod() {
try {
int sum = 100 / 0;
} catch (Exception e) {
e.printStackTrace();
}
}
複製代碼
關鍵代碼來了。。。
@Before("handler(java.lang.Exception)")
public void handlerMethod() {
Log.e(TAG, "handlerMethod: 異常產生了");
}
複製代碼
是否是很簡單,使用一個handler關鍵字,加上一個異常的全路徑,ok搞定,可是這裏必定要注意,前面的註解只能是@Before,切記!!!
有許多第三方你是不知道具體方法名稱的,可是你還想使用的話怎麼辦?那就是註解了,由於註解能夠很好的解決這種需求。
再來看一段代碼:
定義一段註解內容:
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}
複製代碼
由於註解的這些內容不是本篇文章的重點,因此這裏不許備講解了。感興趣的你能夠百度一下,這個註解主要是在編譯完成後也會起做用,而且是方法和成員變量均可以使用!
在加上下面這段代碼就能夠進行相應切面的操做了!
@Pointcut("execution(@com.jinlong.aspectjdemo.DebugTrace * *(..))")
public void DebugTraceMethod() {
}
@Before("DebugTraceMethod()")
public void beforeDebugTraceMethod(JoinPoint joinPoint) {
String key = joinPoint.getSignature().toString();
Log.e(TAG, "註解這個方法執行了: ");
}
複製代碼
看到在這段代碼你們應該不怎麼陌生了,就是在方法內容添加相應的切面操做。最後在使用的方法的上面添加相應的註解就能夠了!就這麼簡單!
@DebugTrace
private void mothod1() {
Log.e(TAG, "方法1執行了");
}
複製代碼
上面基本上包含了咱們APP使用中,能用到一些內容,若是有什麼講的不到位的地方還請指出。由於是第一次接觸這個東西,可能有些細節講解的不是很到位,還請諒解!!!
想看源碼嗎?想看連接嗎?點這裏